Class: ASHRAE9012019

Inherits:
ASHRAE901 show all
Includes:
ASHRAE9012019CoolingTower
Defined in:
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Pump.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanOnOff.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ThermalZone.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.BoilerHotWater.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoilHeatingGas.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.elevators.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.WaterHeaterMixed.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanVariableVolume.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ZoneHVACComponent.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanConstantVolume.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanVariableVolume.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ChillerElectricEIR.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerTwoSpeed.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerSingleSpeed.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerVariableSpeed.rb,
lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb,
lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb

Overview

This class holds methods that apply ASHRAE 90.1-2019 to a given model.

Constant Summary

Constants inherited from Standard

Standard::STANDARDS_LIST

Instance Attribute Summary collapse

Attributes inherited from Standard

#space_multiplier_map, #standards_data

Pump collapse

Space collapse

Model collapse

FanOnOff collapse

AirLoopHVAC collapse

ThermalZone collapse

hvac_systems collapse

elevators collapse

FanVariableVolume collapse

ZoneHVACComponent collapse

FanConstantVolume collapse

AirTerminalSingleDuctVAVReheat collapse

Instance Method Summary collapse

Methods included from ASHRAE9012019CoolingTower

#cooling_tower_apply_minimum_power_per_flow_gpm_limit

Methods inherited from Standard

#adjust_sizing_system, #afue_to_thermal_eff, #air_loop_hvac_add_motorized_oa_damper, #air_loop_hvac_adjust_minimum_vav_damper_positions_outpatient, #air_loop_hvac_allowable_system_brake_horsepower, #air_loop_hvac_apply_baseline_fan_pressure_rise, #air_loop_hvac_apply_economizer_integration, #air_loop_hvac_apply_economizer_limits, #air_loop_hvac_apply_energy_recovery_ventilator, #air_loop_hvac_apply_energy_recovery_ventilator_efficiency, #air_loop_hvac_apply_maximum_reheat_temperature, #air_loop_hvac_apply_minimum_vav_damper_positions, #air_loop_hvac_apply_multizone_vav_outdoor_air_sizing, #air_loop_hvac_apply_prm_baseline_controls, #air_loop_hvac_apply_prm_baseline_economizer, #air_loop_hvac_apply_prm_baseline_fan_power, #air_loop_hvac_apply_prm_sizing_temperatures, #air_loop_hvac_apply_single_zone_controls, #air_loop_hvac_apply_standard_controls, #air_loop_hvac_apply_vav_damper_action, #air_loop_hvac_data_center_area_served, #air_loop_hvac_dcv_required_when_erv, #air_loop_hvac_demand_control_ventilation_required?, #air_loop_hvac_disable_multizone_vav_optimization, #air_loop_hvac_dx_cooling?, #air_loop_hvac_economizer?, #air_loop_hvac_economizer_required?, #air_loop_hvac_enable_demand_control_ventilation, #air_loop_hvac_enable_multizone_vav_optimization, #air_loop_hvac_enable_optimum_start, #air_loop_hvac_enable_supply_air_temperature_reset_delta, #air_loop_hvac_enable_supply_air_temperature_reset_outdoor_temperature, #air_loop_hvac_enable_supply_air_temperature_reset_warmest_zone, #air_loop_hvac_enable_unoccupied_fan_shutoff, #air_loop_hvac_energy_recovery?, #air_loop_hvac_energy_recovery_ventilator_heat_exchanger_type, #air_loop_hvac_energy_recovery_ventilator_required?, #air_loop_hvac_energy_recovery_ventilator_type, #air_loop_hvac_fan_power_limitation_pressure_drop_adjustment_brake_horsepower, #air_loop_hvac_find_design_supply_air_flow_rate, #air_loop_hvac_floor_area_served, #air_loop_hvac_floor_area_served_exterior_zones, #air_loop_hvac_floor_area_served_interior_zones, #air_loop_hvac_get_occupancy_schedule, #air_loop_hvac_get_relief_fan_power, #air_loop_hvac_get_return_fan_power, #air_loop_hvac_get_supply_fan, #air_loop_hvac_get_supply_fan_power, #air_loop_hvac_has_parallel_piu_air_terminals?, #air_loop_hvac_has_simple_transfer_air?, #air_loop_hvac_humidifier_count, #air_loop_hvac_include_cooling_coil?, #air_loop_hvac_include_economizer?, #air_loop_hvac_include_evaporative_cooler?, #air_loop_hvac_include_hydronic_cooling_coil?, #air_loop_hvac_include_unitary_system?, #air_loop_hvac_include_wshp?, #air_loop_hvac_minimum_zone_ventilation_efficiency, #air_loop_hvac_motorized_oa_damper_required?, #air_loop_hvac_multi_stage_dx_cooling?, #air_loop_hvac_multizone_vav_system?, #air_loop_hvac_optimum_start_required?, #air_loop_hvac_prm_baseline_economizer_required?, #air_loop_hvac_prm_economizer_type_and_limits, #air_loop_hvac_remove_erv, #air_loop_hvac_remove_motorized_oa_damper, #air_loop_hvac_residential_area_served, #air_loop_hvac_return_air_plenum, #air_loop_hvac_set_minimum_damper_position, #air_loop_hvac_set_vsd_curve_type, #air_loop_hvac_static_pressure_reset_required?, #air_loop_hvac_supply_return_exhaust_relief_fans, #air_loop_hvac_system_fan_brake_horsepower, #air_loop_hvac_system_multiplier, #air_loop_hvac_terminal_reheat?, #air_loop_hvac_total_cooling_capacity, #air_loop_hvac_unitary_system?, #air_loop_hvac_unoccupied_fan_shutoff_required?, #air_loop_hvac_unoccupied_threshold, #air_loop_hvac_vav_damper_action, #air_loop_hvac_vav_system?, #air_terminal_single_duct_parallel_piu_reheat_apply_minimum_primary_airflow_fraction, #air_terminal_single_duct_parallel_piu_reheat_apply_prm_baseline_fan_power, #air_terminal_single_duct_parallel_piu_reheat_fan_on_flow_fraction, #air_terminal_single_duct_parallel_reheat_piu_minimum_primary_airflow_fraction, #air_terminal_single_duct_vav_reheat_apply_minimum_damper_position, #air_terminal_single_duct_vav_reheat_reheat_type, #air_terminal_single_duct_vav_reheat_set_heating_cap, #apply_lighting_schedule, #apply_limit_to_subsurface_ratio, #boiler_hot_water_apply_efficiency_and_curves, #boiler_hot_water_find_capacity, #boiler_hot_water_find_design_water_flow_rate, #boiler_hot_water_find_search_criteria, #boiler_hot_water_standard_minimum_thermal_efficiency, build, #chiller_electric_eir_apply_efficiency_and_curves, #chiller_electric_eir_find_capacity, #chiller_electric_eir_find_search_criteria, #chiller_electric_eir_standard_minimum_full_load_efficiency, #chw_sizing_control, #coil_cooling_dx_multi_speed_apply_efficiency_and_curves, #coil_cooling_dx_multi_speed_find_capacity, #coil_cooling_dx_multi_speed_standard_minimum_cop, #coil_cooling_dx_single_speed_apply_efficiency_and_curves, #coil_cooling_dx_single_speed_find_capacity, #coil_cooling_dx_single_speed_standard_minimum_cop, #coil_cooling_dx_two_speed_apply_efficiency_and_curves, #coil_cooling_dx_two_speed_find_capacity, #coil_cooling_dx_two_speed_standard_minimum_cop, #coil_cooling_water_to_air_heat_pump_apply_efficiency_and_curves, #coil_cooling_water_to_air_heat_pump_find_capacity, #coil_cooling_water_to_air_heat_pump_standard_minimum_cop, #coil_heating_dx_multi_speed_apply_efficiency_and_curves, #coil_heating_dx_single_speed_apply_defrost_eir_curve_limits, #coil_heating_dx_single_speed_apply_efficiency_and_curves, #coil_heating_dx_single_speed_find_capacity, #coil_heating_dx_single_speed_standard_minimum_cop, #coil_heating_gas_apply_efficiency_and_curves, #coil_heating_gas_apply_prototype_efficiency, #coil_heating_gas_find_capacity, #coil_heating_gas_multi_stage_apply_efficiency_and_curves, #coil_heating_gas_multi_stage_find_capacity, #coil_heating_gas_multi_stage_find_search_criteria, #coil_heating_water_to_air_heat_pump_apply_efficiency_and_curves, #coil_heating_water_to_air_heat_pump_find_capacity, #coil_heating_water_to_air_heat_pump_standard_minimum_cop, #combustion_eff_to_thermal_eff, #controller_water_coil_set_convergence_limits, #convert_curve_biquadratic, #cooling_tower_single_speed_apply_efficiency_and_curves, #cooling_tower_two_speed_apply_efficiency_and_curves, #cop_heating_to_cop_heating_no_fan, #cop_no_fan_to_eer, #cop_no_fan_to_seer, #cop_to_eer, #cop_to_kw_per_ton, #cop_to_seer, #create_air_conditioner_variable_refrigerant_flow, #create_boiler_hot_water, #create_central_air_source_heat_pump, #create_coil_cooling_dx_single_speed, #create_coil_cooling_dx_two_speed, #create_coil_cooling_water, #create_coil_cooling_water_to_air_heat_pump_equation_fit, #create_coil_heating_dx_single_speed, #create_coil_heating_electric, #create_coil_heating_gas, #create_coil_heating_water, #create_coil_heating_water_to_air_heat_pump_equation_fit, #create_curve_bicubic, #create_curve_biquadratic, #create_curve_cubic, #create_curve_exponent, #create_curve_quadratic, #create_fan_constant_volume, #create_fan_constant_volume_from_json, #create_fan_on_off, #create_fan_on_off_from_json, #create_fan_variable_volume, #create_fan_variable_volume_from_json, #create_fan_zone_exhaust, #create_fan_zone_exhaust_from_json, #define_space_multiplier, #eer_to_cop, #eer_to_cop_no_fan, #ems_friendly_name, #enthalpy_recovery_ratio_design_to_typical_adjustment, #fan_constant_volume_apply_prototype_fan_pressure_rise, #fan_on_off_apply_prototype_fan_pressure_rise, #fan_variable_volume_apply_prototype_fan_pressure_rise, #fan_variable_volume_cooling_system_type, #fan_variable_volume_part_load_fan_power_limitation?, #fan_variable_volume_set_control_type, #fan_zone_exhaust_apply_prototype_fan_pressure_rise, #find_exposed_conditioned_roof_surfaces, #find_exposed_conditioned_vertical_surfaces, #find_highest_roof_centre, #fluid_cooler_apply_minimum_power_per_flow, #get_avg_of_other_zones, #get_default_surface_cons_from_surface_type, #get_fan_object_for_airloop, #get_fan_schedule_for_each_zone, #get_group_heat_types, #get_outdoor_subsurface_ratio, #get_weekday_values_from_8760, #get_wtd_avg_of_other_zones, #headered_pumps_variable_speed_set_control_type, #heat_exchanger_air_to_air_sensible_and_latent_apply_effectiveness, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_efficiency, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_efficiency_enthalpy_recovery_ratio, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_nominal_electric_power, #heat_exchanger_air_to_air_sensible_and_latent_enthalpy_recovery_ratio_to_effectiveness, #heat_exchanger_air_to_air_sensible_and_latent_minimum_effectiveness, #heat_exchanger_air_to_air_sensible_and_latent_prototype_default_fan_efficiency, #hspf_to_cop, #hspf_to_cop_no_fan, #interior_lighting_get_prm_data, #kw_per_ton_to_cop, #load_hvac_map, #load_initial_osm, #make_ruleset_sched_from_8760, #make_week_ruleset_sched_from_168, #model_add_baseboard, #model_add_cav, #model_add_central_air_source_heat_pump, #model_add_chw_loop, #model_add_construction, #model_add_construction_set, #model_add_crac, #model_add_crah, #model_add_curve, #model_add_cw_loop, #model_add_data_center_hvac, #model_add_data_center_load, #model_add_daylighting_controls, #model_add_district_ambient_loop, #model_add_doas, #model_add_doas_cold_supply, #model_add_elevator, #model_add_elevators, #model_add_evap_cooler, #model_add_exhaust_fan, #model_add_four_pipe_fan_coil, #model_add_furnace_central_ac, #model_add_ground_hx_loop, #model_add_high_temp_radiant, #model_add_hp_loop, #model_add_hvac, #model_add_hvac_system, #model_add_hw_loop, #model_add_ideal_air_loads, #model_add_low_temp_radiant, #model_add_material, #model_add_minisplit_hp, #model_add_plant_supply_water_temperature_control, #model_add_prm_baseline_system, #model_add_prm_elevators, #model_add_psz_ac, #model_add_psz_vav, #model_add_ptac, #model_add_pthp, #model_add_pvav, #model_add_pvav_pfp_boxes, #model_add_radiant_basic_controls, #model_add_radiant_proportional_controls, #model_add_refrigeration_case, #model_add_refrigeration_compressor, #model_add_refrigeration_system, #model_add_refrigeration_walkin, #model_add_residential_erv, #model_add_residential_ventilator, #model_add_schedule, #model_add_split_ac, #model_add_swh, #model_add_swh_end_uses_by_space, #model_add_transformer, #model_add_typical_exterior_lights, #model_add_typical_refrigeration, #model_add_typical_swh, #model_add_unitheater, #model_add_vav_pfp_boxes, #model_add_vav_reheat, #model_add_vrf, #model_add_water_source_hp, #model_add_waterside_economizer, #model_add_window_ac, #model_add_zone_erv, #model_add_zone_heat_cool_request_count_program, #model_add_zone_ventilation, #model_apply_baseline_exterior_lighting, #model_apply_hvac_efficiency_standard, #model_apply_infiltration_standard, #model_apply_multizone_vav_outdoor_air_sizing, #model_apply_prm_baseline_sizing_schedule, #model_apply_prm_baseline_skylight_to_roof_ratio, #model_apply_prm_baseline_window_to_wall_ratio, #model_apply_prm_construction_types, #model_apply_prm_sizing_parameters, #model_apply_standard_constructions, #model_apply_standard_infiltration, #model_baseline_system_vav_fan_type, #model_create_exterior_lighting_area_length_count_hash, #model_create_multizone_fan_schedule, #model_create_prm_any_baseline_building, #model_create_prm_baseline_building, #model_create_prm_baseline_building_requires_proposed_model_sizing_run, #model_create_prm_baseline_building_requires_vlt_sizing_run, #model_create_prm_proposed_building, #model_create_prm_stable_baseline_building, #model_create_space_type_hash, #model_create_story_hash, #model_differentiate_primary_secondary_thermal_zones, #model_effective_num_stories, #model_elevator_lift_power, #model_eliminate_outlier_zones, #model_find_and_add_construction, #model_find_ashrae_hot_water_demand, #model_find_climate_zone_set, #model_find_icc_iecc_2015_hot_water_demand, #model_find_icc_iecc_2015_internal_loads, #model_find_object, #model_find_objects, #model_find_prototype_floor_area, #model_find_target_eui, #model_find_target_eui_by_end_use, #model_find_water_heater_capacity_volume_and_parasitic, #model_get_baseline_system_type_by_zone, #model_get_building_properties, #model_get_climate_zone_set_from_list, #model_get_construction_properties, #model_get_construction_set, #model_get_district_heating_zones, #model_get_lookup_name, #model_get_or_add_ambient_water_loop, #model_get_or_add_chilled_water_loop, #model_get_or_add_ground_hx_loop, #model_get_or_add_heat_pump_loop, #model_get_or_add_hot_water_loop, #model_is_hvac_autosized, #model_legacy_results_by_end_use_and_fuel_type, #model_make_name, #model_prm_baseline_system_change_fuel_type, #model_prm_baseline_system_groups, #model_prm_baseline_system_number, #model_prm_baseline_system_type, #model_prm_skylight_to_roof_ratio_limit, #model_process_results_for_datapoint, #model_remap_office, #model_remove_external_shading_devices, #model_remove_prm_ems_objects, #model_remove_prm_hvac, #model_remove_unused_resource_objects, #model_set_vav_terminals_to_control_for_outdoor_air, #model_system_outdoor_air_sizing_vrp_method, #model_two_pipe_loop, #model_typical_display_case_zone, #model_typical_hvac_system_type, #model_typical_walkin_zone, #model_validate_standards_spacetypes_in_model, #model_ventilation_method, #model_walkin_freezer_latent_case_credit_curve, #model_zones_with_occ_and_fuel_type, #planar_surface_apply_standard_construction, #plant_loop_adiabatic_pipes_only, #plant_loop_apply_prm_baseline_chilled_water_pumping_type, #plant_loop_apply_prm_baseline_chilled_water_temperatures, #plant_loop_apply_prm_baseline_condenser_water_pumping_type, #plant_loop_apply_prm_baseline_condenser_water_temperatures, #plant_loop_apply_prm_baseline_hot_water_pumping_type, #plant_loop_apply_prm_baseline_hot_water_temperatures, #plant_loop_apply_prm_baseline_pump_power, #plant_loop_apply_prm_baseline_pumping_type, #plant_loop_apply_prm_baseline_temperatures, #plant_loop_apply_prm_number_of_boilers, #plant_loop_apply_prm_number_of_chillers, #plant_loop_apply_prm_number_of_cooling_towers, #plant_loop_apply_standard_controls, #plant_loop_capacity_w_by_maxflow_and_delta_t_forwater, #plant_loop_enable_supply_water_temperature_reset, #plant_loop_find_maximum_loop_flow_rate, #plant_loop_prm_baseline_condenser_water_temperatures, #plant_loop_set_chw_pri_sec_configuration, #plant_loop_supply_water_temperature_reset_required?, #plant_loop_swh_loop?, #plant_loop_swh_system_type, #plant_loop_total_cooling_capacity, #plant_loop_total_floor_area_served, #plant_loop_total_heating_capacity, #plant_loop_total_rated_w_per_gpm, #plant_loop_variable_flow_system?, #prototype_apply_condenser_water_temperatures, #prototype_condenser_water_temperatures, #pump_variable_speed_control_type, #pump_variable_speed_set_control_type, register_standard, #remove_air_loops, #remove_all_hvac, #remove_all_plant_loops, #remove_all_zone_equipment, #remove_hvac, #remove_plant_loops, #remove_unused_curves, #remove_vrf, #remove_zone_equipment, #rename_air_loop_nodes, #rename_plant_loop_nodes, #safe_load_model, #seer_to_cop, #seer_to_cop_no_fan, #space_add_daylighting_controls, #space_apply_infiltration_rate, #space_conditioning_category, #space_daylighted_areas, #space_get_equip_annual_array, #space_get_loads_for_all_equips, #space_internal_load_annual_array, #space_occupancy_annual_array, #space_remove_daylighting_controls, #space_set_baseline_daylighting_controls, #space_sidelighting_effective_aperture, #space_skylight_effective_aperture, #space_type_apply_int_loads_prm, #space_type_apply_internal_load_schedules, #space_type_apply_internal_loads, #space_type_apply_rendering_color, #space_type_get_construction_properties, #space_type_get_standards_data, #space_type_light_sch_change, #standard_design_sizing_temperatures, #standards_lookup_table_first, #standards_lookup_table_many, #strip_model, #sub_surface_create_centered_subsurface_from_scaled_surface, #sub_surface_create_scaled_subsurfaces_from_surface, #surface_adjust_fenestration_in_a_surface, #surface_subsurface_ua, #thermal_eff_to_afue, #thermal_eff_to_comb_eff, #thermal_zone_add_exhaust, #thermal_zone_add_exhaust_fan_dcv, #thermal_zone_apply_prm_baseline_supply_temperatures, #thermal_zone_conditioning_category, #thermal_zone_demand_control_ventilation_required?, #thermal_zone_exhaust_fan_dcv_required?, #thermal_zone_fossil_or_electric_type, #thermal_zone_get_annual_operating_hours, #thermal_zone_get_zone_fuels_for_occ_and_fuel_type, #thermal_zone_infer_system_type, #thermal_zone_occupancy_eflh, #thermal_zone_peak_internal_load, #thermal_zone_prm_baseline_cooling_design_supply_temperature, #thermal_zone_prm_baseline_heating_design_supply_temperature, #thermal_zone_prm_lab_delta_t, #thermal_zone_prm_unitheater_design_supply_temperature, #true?, #validate_initial_model, #water_heater_convert_energy_factor_to_thermal_efficiency_and_ua, #water_heater_convert_uniform_energy_factor_to_energy_factor, #water_heater_determine_sub_type, #water_heater_mixed_apply_efficiency, #water_heater_mixed_apply_prm_baseline_fuel_type, #water_heater_mixed_find_capacity, #water_heater_mixed_get_efficiency_requirement, #zone_hvac_component_apply_prm_baseline_fan_power, #zone_hvac_component_apply_standard_controls, #zone_hvac_component_apply_vestibule_heating_control, #zone_hvac_component_occupancy_ventilation_control, #zone_hvac_component_prm_baseline_fan_efficacy, #zone_hvac_get_fan_object, #zone_hvac_unoccupied_threshold

Methods included from PrototypeFan

apply_base_fan_variables, #create_fan_by_name, #get_fan_from_standards, #lookup_fan_curve_coefficients_from_json, #prototype_fan_apply_prototype_fan_efficiency

Methods included from CoilDX

#coil_dx_find_search_criteria, #coil_dx_heat_pump?, #coil_dx_heating_type, #coil_dx_subcategory

Methods included from CoolingTower

#cooling_tower_apply_minimum_power_per_flow, #cooling_tower_apply_minimum_power_per_flow_gpm_limit

Methods included from Pump

#pump_apply_prm_pressure_rise_and_motor_efficiency, #pump_apply_standard_minimum_motor_efficiency, #pump_brake_horsepower, #pump_motor_horsepower, #pump_pumppower, #pump_rated_w_per_gpm

Methods included from Fan

#fan_adjust_pressure_rise_to_meet_fan_power, #fan_apply_standard_minimum_motor_efficiency, #fan_baseline_impeller_efficiency, #fan_brake_horsepower, #fan_change_impeller_efficiency, #fan_change_motor_efficiency, #fan_design_air_flow, #fan_fanpower, #fan_motor_horsepower, #fan_rated_w_per_cfm, #fan_small_fan?, #fan_standard_minimum_motor_efficiency_and_size

Constructor Details

#initializeASHRAE9012019

Returns a new instance of ASHRAE9012019.



8
9
10
11
12
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.rb', line 8

def initialize
  super()
  @template = '90.1-2019'
  load_standards_database
end

Instance Attribute Details

#templateObject (readonly)

Returns the value of attribute template.



6
7
8
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.rb', line 6

def template
  @template
end

Instance Method Details

#air_loop_hvac_adjust_minimum_vav_damper_positions(air_loop_hvac) ⇒ Boolean

TODO:

Add exception logic for systems serving parking garage, warehouse, or multifamily

Adjust minimum VAV damper positions and set minimum design system outdoor air flow following ASHRAE Std. 62.1-2019

Returns:

  • (Boolean)

    Returns true if required, false if not.



562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 562

def air_loop_hvac_adjust_minimum_vav_damper_positions(air_loop_hvac)
  # Do not apply the adjustment to some of the system in
  # the hospital and outpatient which have their minimum
  # damper position determined based on AIA 2001 ventilation
  # requirements
  if (@instvarbuilding_type == 'Hospital' && (air_loop_hvac.name.to_s.include?('VAV_ER') || air_loop_hvac.name.to_s.include?('VAV_ICU') ||
                                              air_loop_hvac.name.to_s.include?('VAV_OR') || air_loop_hvac.name.to_s.include?('VAV_LABS') ||
                                              air_loop_hvac.name.to_s.include?('VAV_PATRMS'))) ||
     (@instvarbuilding_type == 'Outpatient' && air_loop_hvac.name.to_s.include?('Outpatient F1'))

    return true
  end

  # Total uncorrected outdoor airflow rate
  v_ou = 0.0
  air_loop_hvac.thermalZones.each do |zone|
    # Vou is the system uncorrected outdoor airflow:
    # Zone airflow is multiplied by the zone multiplier
    v_ou += OpenstudioStandards::ThermalZone.thermal_zone_get_outdoor_airflow_rate(zone) * zone.multiplier.to_f
  end

  v_ou_cfm = OpenStudio.convert(v_ou, 'm^3/s', 'cfm').get

  OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: v_ou = #{v_ou_cfm.round} cfm.")

  # Retrieve the sum of the zone minimum primary airflow
  if air_loop_hvac.model.version < OpenStudio::VersionString.new('3.6.0')
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', 'Required AirLoopHVAC method .autosizedSumMinimumHeatingAirFlowRates is not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
  else
    vpz_min_sum = air_loop_hvac.autosizedSumMinimumHeatingAirFlowRates
  end

  air_loop_hvac.thermalZones.sort.each do |zone|
    # Breathing zone airflow rate
    v_bz = OpenstudioStandards::ThermalZone.thermal_zone_get_outdoor_airflow_rate(zone)

    # Zone air distribution, assumed 1 per PNNL
    e_z = 1.0

    # Zone airflow rate
    v_oz = v_bz / e_z

    # Primary design airflow rate
    # max of heating and cooling
    # design air flow rates
    v_pz = 0.0

    # error if zone autosized methods are not available
    if air_loop_hvac.model.version < OpenStudio::VersionString.new('3.6.0')
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', 'Required ThermalZone methods .autosizedCoolingDesignAirFlowRate and .autosizedHeatingDesignAirFlowRate are not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
    end

    clg_dsn_flow = zone.autosizedCoolingDesignAirFlowRate
    if clg_dsn_flow.is_initialized
      clg_dsn_flow = clg_dsn_flow.get
      if clg_dsn_flow > v_pz
        v_pz = clg_dsn_flow
      end
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "For #{air_loop_hvac.name}: #{zone.name} clg_dsn_flow could not be found.")
    end
    htg_dsn_flow = zone.autosizedHeatingDesignAirFlowRate
    if htg_dsn_flow.is_initialized
      htg_dsn_flow = htg_dsn_flow.get
      if htg_dsn_flow > v_pz
        v_pz = htg_dsn_flow
      end
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "For #{air_loop_hvac.name}: #{zone.name} htg_dsn_flow could not be found.")
    end

    # Zone ventilation efficiency calculation is computed
    # on a per zone basis, the zone primary airflow is
    # adjusted to removed the zone multiplier
    v_pz /= zone.multiplier.to_f

    # Set minimum damper position
    air_loop_hvac_set_minimum_damper_position(zone, [0.01, [1.5 * v_oz / v_pz, 1.0].min].max.round(3))
  end

  # Occupant diversity (D): Ps / sum(Pz)
  # Current value is based on school prototypes
  # which are assumed to have the most diversity
  occ_diver_d = 0.66

  # From ASHRAE Std 62.1-2019 Section 6.2.5.3
  if occ_diver_d < 0.6
    e_v = (0.88 * occ_diver_d) + 0.22
  else
    e_v = 0.75
  end

  # Total system outdoor intake flow rate
  v_ot = v_ou / e_v
  v_ot_cfm = OpenStudio.convert(v_ot, 'm^3/s', 'cfm').get

  # Get maximum OA fraction schedule
  oa_ctrl = air_loop_hvac.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir
  max_oa_frac_sch = oa_ctrl.maximumFractionofOutdoorAirSchedule

  if max_oa_frac_sch.is_initialized
    max_oa_frac_sch = max_oa_frac_sch.get
    if max_oa_frac_sch.to_ScheduleRuleset.is_initialized
      max_oa_frac_sch = max_oa_frac_sch.to_ScheduleRuleset.get
      max_oa_frac_sch_type = 'Schedule:Year'
    elsif max_oa_frac_sch.to_ScheduleConstant.is_initialized
      max_oa_frac_sch = max_oa_frac_sch.to_ScheduleConstant.get
      max_oa_frac_sch_type = 'Schedule:Constant'
    elsif max_oa_frac_sch.to_ScheduleCompact.is_initialized
      max_oa_frac_sch = max_oa_frac_sch.to_ScheduleCompact.get
      max_oa_frac_sch_type = 'Schedule:Compact'
    end
  else
    max_oa_frac_sch = OpenStudio::Model::ScheduleConstant.new(air_loop_hvac.model)
    max_oa_frac_sch.setName("#{air_loop_hvac.name}_MAX_OA_FRAC")
    max_oa_frac_sch.setValue(1.0)
    max_oa_frac_sch_type = 'Schedule:Constant'
    oa_ctrl.setMaximumFractionofOutdoorAirSchedule(max_oa_frac_sch)
  end

  # Add EMS to "cap" the OA calculated by the
  # Controller:MechanicalVentilation object
  # to the design v_ot using the maximum OA
  # fraction schedule
  # In newer EnergyPlus versions, this is handled by Standard62.1VentilationRateProcedureWithLimit
  # in the Controller:MechanicalVentilation object
  if air_loop_hvac.model.version < OpenStudio::VersionString.new('3.3.0')
    # Add EMS sensors
    # OA mass flow calculated by the Controller:MechanicalVentilation
    air_loop_hvac_name_ems = ems_friendly_name(air_loop_hvac.name)
    oa_vrp_mass_flow = OpenStudio::Model::EnergyManagementSystemSensor.new(air_loop_hvac.model, 'Air System Outdoor Air Mechanical Ventilation Requested Mass Flow Rate')
    oa_vrp_mass_flow.setKeyName(air_loop_hvac.name.to_s)
    oa_vrp_mass_flow.setName("#{air_loop_hvac_name_ems}_OA_VRP")
    # Actual sensed OA mass flow
    oa_mass_flow = OpenStudio::Model::EnergyManagementSystemSensor.new(air_loop_hvac.model, 'Air System Outdoor Air Mass Flow Rate')
    oa_mass_flow.setKeyName(air_loop_hvac.name.to_s)
    oa_mass_flow.setName("#{air_loop_hvac_name_ems}_OA")
    # Actual sensed volumetric OA flow
    oa_vol_flow = OpenStudio::Model::EnergyManagementSystemSensor.new(air_loop_hvac.model, 'System Node Standard Density Volume Flow Rate')
    oa_vol_flow.setKeyName("#{air_loop_hvac.name} Mixed Air Node")
    oa_vol_flow.setName("#{air_loop_hvac_name_ems}_SUPPLY_FLOW")

    # Add EMS actuator
    max_oa_fraction = OpenStudio::Model::EnergyManagementSystemActuator.new(max_oa_frac_sch, max_oa_frac_sch_type, 'Schedule Value')
    max_oa_fraction.setName("#{air_loop_hvac_name_ems}_MAX_OA_FRAC")

    # Add EMS program
    max_oa_ems_prog = OpenStudio::Model::EnergyManagementSystemProgram.new(air_loop_hvac.model)
    max_oa_ems_prog.setName("#{air_loop_hvac.name}_MAX_OA_FRAC")
    max_oa_ems_prog_body = <<-EMS
    IF #{air_loop_hvac_name_ems}_OA > #{air_loop_hvac_name_ems}_OA_VRP,
    SET #{air_loop_hvac_name_ems}_MAX_OA_FRAC = NULL,
    ELSE,
    IF #{air_loop_hvac_name_ems}_SUPPLY_FLOW > 0,
    SET #{air_loop_hvac_name_ems}_MAX_OA_FRAC = #{v_ot} / #{air_loop_hvac_name_ems}_SUPPLY_FLOW,
    ELSE,
    SET #{air_loop_hvac_name_ems}_MAX_OA_FRAC = NULL,
    ENDIF,
    ENDIF
    EMS
    max_oa_ems_prog.setBody(max_oa_ems_prog_body)

    max_oa_ems_prog_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(air_loop_hvac.model)
    max_oa_ems_prog_manager.setName("SET_#{air_loop_hvac.name.to_s.gsub(' ', '_')}_MAX_OA_FRAC")
    max_oa_ems_prog_manager.setCallingPoint('InsideHVACSystemIterationLoop')
    max_oa_ems_prog_manager.addProgram(max_oa_ems_prog)
  end

  # Hard-size the sizing:system
  # object with the calculated min OA flow rate
  sizing_system = air_loop_hvac.sizingSystem
  sizing_system.setDesignOutdoorAirFlowRate(v_ot)
  sizing_system.setSystemOutdoorAirMethod('ZoneSum')

  return true
end

#air_loop_hvac_demand_control_ventilation_limits(air_loop_hvac) ⇒ Array<Double>

Determines the OA flow rates above which an economizer is required. Two separate rates, one for systems with an economizer and another for systems without.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

Returns:

  • (Array<Double>)
    min_oa_without_economizer_cfm, min_oa_with_economizer_cfm


264
265
266
267
268
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 264

def air_loop_hvac_demand_control_ventilation_limits(air_loop_hvac)
  min_oa_without_economizer_cfm = 3000
  min_oa_with_economizer_cfm = 750
  return [min_oa_without_economizer_cfm, min_oa_with_economizer_cfm]
end

#air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone) ⇒ Array<Double>

Determine the limits for the type of economizer present on the AirLoopHVAC, if any.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Array<Double>)
    drybulb_limit_f, enthalpy_limit_btu_per_lb, dewpoint_limit_f


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 41

def air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone)
  drybulb_limit_f = nil
  enthalpy_limit_btu_per_lb = nil
  dewpoint_limit_f = nil

  # Get the OA system and OA controller
  oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem
  return [nil, nil, nil] unless oa_sys.is_initialized

  oa_sys = oa_sys.get
  oa_control = oa_sys.getControllerOutdoorAir
  economizer_type = oa_control.getEconomizerControlType
  oa_control.resetEconomizerMinimumLimitDryBulbTemperature

  case economizer_type
  when 'NoEconomizer'
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name} no economizer")
    return [nil, nil, nil]
  when 'FixedDryBulb'
    search_criteria = {
      'template' => template,
      'climate_zone' => climate_zone
    }
    econ_limits = model_find_object(standards_data['economizers'], search_criteria)
    drybulb_limit_f = econ_limits['fixed_dry_bulb_high_limit_shutoff_temp']
  when 'FixedEnthalpy'
    enthalpy_limit_btu_per_lb = 28.0
  when 'FixedDewPointAndDryBulb'
    drybulb_limit_f = 75.0
    dewpoint_limit_f = 55.0
  when 'DifferentialDryBulb', 'DifferentialEnthalpy'
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Economizer type = #{economizer_type}, no limits defined.")
  end

  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Economizer type = #{economizer_type}, limits [#{drybulb_limit_f},#{enthalpy_limit_btu_per_lb},#{dewpoint_limit_f}]")

  return [drybulb_limit_f, enthalpy_limit_btu_per_lb, dewpoint_limit_f]
end

#air_loop_hvac_economizer_type_allowable?(air_loop_hvac, climate_zone) ⇒ Boolean

Check the economizer type currently specified in the ControllerOutdoorAir object on this air loop is acceptable per the standard.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Boolean)

    Returns true if allowable, if the system has no economizer or no OA system. Returns false if the economizer type is not allowable.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 97

def air_loop_hvac_economizer_type_allowable?(air_loop_hvac, climate_zone)
  # EnergyPlus economizer types
  # 'NoEconomizer'
  # 'FixedDryBulb'
  # 'FixedEnthalpy'
  # 'DifferentialDryBulb'
  # 'DifferentialEnthalpy'
  # 'FixedDewPointAndDryBulb'
  # 'ElectronicEnthalpy'
  # 'DifferentialDryBulbAndEnthalpy'

  # Get the OA system and OA controller
  oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem
  return true unless oa_sys.is_initialized

  oa_sys = oa_sys.get
  oa_control = oa_sys.getControllerOutdoorAir
  economizer_type = oa_control.getEconomizerControlType

  # Return true if no economizer is present
  if economizer_type == 'NoEconomizer'
    return true
  end

  # Determine the prohibited types
  prohibited_types = []
  case climate_zone
  when 'ASHRAE 169-2006-0B',
       'ASHRAE 169-2006-1B',
       'ASHRAE 169-2006-2B',
       'ASHRAE 169-2006-3B',
       'ASHRAE 169-2006-3C',
       'ASHRAE 169-2006-4B',
       'ASHRAE 169-2006-4C',
       'ASHRAE 169-2006-5B',
       'ASHRAE 169-2006-6B',
       'ASHRAE 169-2006-7A',
       'ASHRAE 169-2006-7B',
       'ASHRAE 169-2006-8A',
       'ASHRAE 169-2006-8B',
       'ASHRAE 169-2013-0B',
       'ASHRAE 169-2013-1B',
       'ASHRAE 169-2013-2B',
       'ASHRAE 169-2013-3B',
       'ASHRAE 169-2013-3C',
       'ASHRAE 169-2013-4B',
       'ASHRAE 169-2013-4C',
       'ASHRAE 169-2013-5B',
       'ASHRAE 169-2013-6B',
       'ASHRAE 169-2013-7A',
       'ASHRAE 169-2013-7B',
       'ASHRAE 169-2013-8A',
       'ASHRAE 169-2013-8B'
    prohibited_types = ['FixedEnthalpy']
  when 'ASHRAE 169-2006-0A',
       'ASHRAE 169-2006-1A',
       'ASHRAE 169-2006-2A',
       'ASHRAE 169-2006-3A',
       'ASHRAE 169-2006-4A',
       'ASHRAE 169-2013-0A',
       'ASHRAE 169-2013-1A',
       'ASHRAE 169-2013-2A',
       'ASHRAE 169-2013-3A',
       'ASHRAE 169-2013-4A'
    prohibited_types = ['FixedDryBulb', 'DifferentialDryBulb']
  when 'ASHRAE 169-2006-5A',
       'ASHRAE 169-2006-6A',
       'ASHRAE 169-2013-5A',
       'ASHRAE 169-2013-6A'
    prohibited_types = []
  end

  # Check if the specified type is allowed
  economizer_type_allowed = true
  if prohibited_types.include?(economizer_type)
    economizer_type_allowed = false
  end

  return economizer_type_allowed
end

#air_loop_hvac_energy_recovery_ventilator_flow_limit(air_loop_hvac, climate_zone, pct_oa) ⇒ Double

Determine the airflow limits that govern whether or not an ERV is required. Based on climate zone and % OA, plus the number of operating hours the system has.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

  • pct_oa (Double)

    percentage of outdoor air

Returns:

  • (Double)

    the flow rate above which an ERV is required. if nil, ERV is never required.



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 442

def air_loop_hvac_energy_recovery_ventilator_flow_limit(air_loop_hvac, climate_zone, pct_oa)
  # Calculate the number of system operating hours
  # based on the availability schedule.
  ann_op_hrs = 0.0
  avail_sch = air_loop_hvac.availabilitySchedule
  if avail_sch == air_loop_hvac.model.alwaysOnDiscreteSchedule
    ann_op_hrs = 8760.0
  elsif avail_sch.to_ScheduleRuleset.is_initialized
    avail_sch = avail_sch.to_ScheduleRuleset.get
    ann_op_hrs = OpenstudioStandards::Schedules.schedule_ruleset_get_hours_above_value(avail_sch, 0.0)
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "For #{air_loop_hvac.name}: could not determine annual operating hours. Assuming less than 8,000 for ERV determination.")
  end

  if ann_op_hrs < 8000.0
    # Table 6.5.6.1-1, less than 8000 hrs
    search_criteria = {
      'template' => template,
      'climate_zone' => climate_zone,
      'under_8000_hours' => true,
      'nontransient_dwelling' => false,
      'enthalpy_recovery_ratio_design_conditions' => 'Cooling'
    }
    energy_recovery_limits = model_find_object(standards_data['energy_recovery'], search_criteria)
    if energy_recovery_limits.nil?
      # Repeat the search for heating
      search_criteria['enthalpy_recovery_ratio_design_conditions'] = 'Heating'
      energy_recovery_limits = model_find_object(standards_data['energy_recovery'], search_criteria)
      if energy_recovery_limits.nil?
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "Cannot find energy recovery limits for template '#{template}', climate zone '#{climate_zone}', and under 8000 hours, assuming no energy recovery required.")
        return nil
      end
    end

    if pct_oa < 0.1
      erv_cfm = nil
    elsif pct_oa >= 0.1 && pct_oa < 0.2
      erv_cfm = energy_recovery_limits['10_to_20_percent_oa']
    elsif pct_oa >= 0.2 && pct_oa < 0.3
      erv_cfm = energy_recovery_limits['20_to_30_percent_oa']
    elsif pct_oa >= 0.3 && pct_oa < 0.4
      erv_cfm = energy_recovery_limits['30_to_40_percent_oa']
    elsif pct_oa >= 0.4 && pct_oa < 0.5
      erv_cfm = energy_recovery_limits['40_to_50_percent_oa']
    elsif pct_oa >= 0.5 && pct_oa < 0.6
      erv_cfm = energy_recovery_limits['50_to_60_percent_oa']
    elsif pct_oa >= 0.6 && pct_oa < 0.7
      erv_cfm = energy_recovery_limits['60_to_70_percent_oa']
    elsif pct_oa >= 0.7 && pct_oa < 0.8
      erv_cfm = energy_recovery_limits['70_to_80_percent_oa']
    elsif pct_oa >= 0.8
      erv_cfm = energy_recovery_limits['greater_than_80_percent_oa']
    end
  else
    # Check if air loop serves a non-transient dwelling unit,
    # currently non-transient dwelling units are residential
    # spaces in the apartment prototypes
    building_data = model_get_building_properties(air_loop_hvac.model)
    building_type = building_data['building_type']
    nontrans_dwel = false
    if building_type == 'MidriseApartment' || building_type == 'HighriseApartment'
      air_loop_hvac.thermalZones.each do |zone|
        next unless OpenstudioStandards::ThermalZone.thermal_zone_residential?(zone)

        nontrans_dwel = true
      end
    end

    # Table 6.5.6.1-2, above 8000 hrs
    search_criteria = {
      'template' => template,
      'climate_zone' => climate_zone,
      'under_8000_hours' => false,
      'nontransient_dwelling' => nontrans_dwel,
      'enthalpy_recovery_ratio_design_conditions' => 'Cooling'
    }
    energy_recovery_limits = model_find_object(standards_data['energy_recovery'], search_criteria)
    if energy_recovery_limits.nil?
      # Repeat the search for heating
      search_criteria['enthalpy_recovery_ratio_design_conditions'] = 'Heating'
      energy_recovery_limits = model_find_object(standards_data['energy_recovery'], search_criteria)
      if energy_recovery_limits.nil?
        OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "Cannot find energy recovery limits for template '#{template}', climate zone '#{climate_zone}', and under 8000 hours, assuming no energy recovery required.")
        return nil
      end
    end
    if pct_oa < 0.1
      if nontrans_dwel
        erv_cfm = energy_recovery_limits['0_to_10_percent_oa']
      else
        erv_cfm = nil
      end
    elsif pct_oa >= 0.1 && pct_oa < 0.2
      erv_cfm = energy_recovery_limits['10_to_20_percent_oa']
    elsif pct_oa >= 0.2 && pct_oa < 0.3
      erv_cfm = energy_recovery_limits['20_to_30_percent_oa']
    elsif pct_oa >= 0.3 && pct_oa < 0.4
      erv_cfm = energy_recovery_limits['30_to_40_percent_oa']
    elsif pct_oa >= 0.4 && pct_oa < 0.5
      erv_cfm = energy_recovery_limits['40_to_50_percent_oa']
    elsif pct_oa >= 0.5 && pct_oa < 0.6
      erv_cfm = energy_recovery_limits['50_to_60_percent_oa']
    elsif pct_oa >= 0.6 && pct_oa < 0.7
      erv_cfm = energy_recovery_limits['60_to_70_percent_oa']
    elsif pct_oa >= 0.7 && pct_oa < 0.8
      erv_cfm = energy_recovery_limits['70_to_80_percent_oa']
    elsif pct_oa >= 0.8
      erv_cfm = energy_recovery_limits['greater_than_80_percent_oa']
    end
  end

  return erv_cfm
end

#air_loop_hvac_integrated_economizer_required?(air_loop_hvac, climate_zone) ⇒ Boolean

Determine if the system economizer must be integrated or not. All economizers must be integrated in 90.1-2019

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Boolean)

    returns true if required, false if not



86
87
88
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 86

def air_loop_hvac_integrated_economizer_required?(air_loop_hvac, climate_zone)
  return true
end

#air_loop_hvac_motorized_oa_damper_limits(air_loop_hvac, climate_zone) ⇒ Array<Double>

Determine the air flow and number of story limits for whether motorized OA damper is required.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Array<Double>)

    [minimum_oa_flow_cfm, maximum_stories]. If both nil, never required



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 275

def air_loop_hvac_motorized_oa_damper_limits(air_loop_hvac, climate_zone)
  case climate_zone
  when 'ASHRAE 169-2006-0A',
       'ASHRAE 169-2006-1A',
       'ASHRAE 169-2006-0B',
       'ASHRAE 169-2006-1B',
       'ASHRAE 169-2006-2A',
       'ASHRAE 169-2006-2B',
       'ASHRAE 169-2006-3A',
       'ASHRAE 169-2006-3B',
       'ASHRAE 169-2006-3C',
       'ASHRAE 169-2013-0A',
       'ASHRAE 169-2013-1A',
       'ASHRAE 169-2013-0B',
       'ASHRAE 169-2013-1B',
       'ASHRAE 169-2013-2A',
       'ASHRAE 169-2013-2B',
       'ASHRAE 169-2013-3A',
       'ASHRAE 169-2013-3B',
       'ASHRAE 169-2013-3C'
    minimum_oa_flow_cfm = 0
    maximum_stories = 999 # Any number of stories
  else
    minimum_oa_flow_cfm = 0
    maximum_stories = 0
  end

  return [minimum_oa_flow_cfm, maximum_stories]
end

#air_loop_hvac_multizone_vav_optimization_required?(air_loop_hvac, climate_zone) ⇒ Boolean

TODO:

Add exception logic for systems with AIA healthcare ventilation requirements dual duct systems

Note:

code_sections [90.1-2019_6.5.3.3]

Determine if multizone vav optimization is required.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Boolean)

    returns true if required, false if not



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 185

def air_loop_hvac_multizone_vav_optimization_required?(air_loop_hvac, climate_zone)
  multizone_opt_required = false

  # Not required for systems with fan-powered terminals
  num_fan_powered_terminals = 0
  air_loop_hvac.demandComponents.each do |comp|
    if comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized || comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
      num_fan_powered_terminals += 1
    end
  end
  if num_fan_powered_terminals > 0
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, multizone vav optimization is not required because the system has #{num_fan_powered_terminals} fan-powered terminals.")
    return multizone_opt_required
  end

  # Get the OA intake
  controller_oa = nil
  controller_mv = nil
  oa_system = nil
  if air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized
    oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get
    controller_oa = oa_system.getControllerOutdoorAir
    controller_mv = controller_oa.controllerMechanicalVentilation
  else
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, multizone optimization is not applicable because system has no OA intake.")
    return multizone_opt_required
  end

  # Get the AHU design supply air flow rate
  dsn_flow_m3_per_s = nil
  if air_loop_hvac.designSupplyAirFlowRate.is_initialized
    dsn_flow_m3_per_s = air_loop_hvac.designSupplyAirFlowRate.get
  elsif air_loop_hvac.autosizedDesignSupplyAirFlowRate.is_initialized
    dsn_flow_m3_per_s = air_loop_hvac.autosizedDesignSupplyAirFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name} design supply air flow rate is not available, cannot apply efficiency standard.")
    return multizone_opt_required
  end
  dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get

  # Get the minimum OA flow rate
  min_oa_flow_m3_per_s = nil
  if controller_oa.minimumOutdoorAirFlowRate.is_initialized
    min_oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
  elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
    min_oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
    return multizone_opt_required
  end
  min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get

  # Calculate the percent OA at design airflow
  pct_oa = min_oa_flow_m3_per_s / dsn_flow_m3_per_s

  # Not required for systems where
  # exhaust is more than 70% of the total OA intake.
  if pct_oa > 0.7
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: multizone optimization is not applicable because system is more than 70% OA.")
    return multizone_opt_required
  end

  # @todo Not required for dual-duct systems
  # if self.isDualDuct
  # OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because it is a dual duct system")
  # return multizone_opt_required
  # end

  # If here, multizone vav optimization is required
  multizone_opt_required = true

  return multizone_opt_required
end

#air_loop_hvac_single_zone_controls_num_stages(air_loop_hvac, climate_zone) ⇒ Integer

Determine the number of stages that should be used as controls for single zone DX systems. 90.1-2019 depends on the cooling capacity of the system.

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Integer)

    the number of stages: 0, 1, 2



311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 311

def air_loop_hvac_single_zone_controls_num_stages(air_loop_hvac, climate_zone)
  min_clg_cap_btu_per_hr = 65_000
  clg_cap_btu_per_hr = OpenStudio.convert(air_loop_hvac_total_cooling_capacity(air_loop_hvac), 'W', 'Btu/hr').get
  if clg_cap_btu_per_hr >= min_clg_cap_btu_per_hr
    num_stages = 2
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: two-stage control is required since cooling capacity of #{clg_cap_btu_per_hr.round} Btu/hr exceeds the minimum of #{min_clg_cap_btu_per_hr.round} Btu/hr .")
  else
    num_stages = 1
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: two-stage control is not required since cooling capacity of #{clg_cap_btu_per_hr.round} Btu/hr is less than the minimum of #{min_clg_cap_btu_per_hr.round} Btu/hr .")
  end

  return num_stages
end

#air_loop_hvac_standby_mode_occupancy_control(air_loop_hvac, standby_mode_spaces) ⇒ Boolean

Add occupant standby controls to air loop When the thermostat schedule is setup or setback the ventilation is shutoff. Currently this is done by scheduling air terminal dampers (so load can still be met) and cycling unitary system fans

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    OpenStudio AirLoopHVAC object

  • standby_mode_spaces (Array<OpenStudio::Model::Space>)

    List of all spaces required to have standby mode controls

Returns:

  • (Boolean)

    true if sucessful, false otherwise



748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 748

def air_loop_hvac_standby_mode_occupancy_control(air_loop_hvac, standby_mode_spaces)
  if air_loop_hvac_include_unitary_system?(air_loop_hvac)
    unitary_system = nil
    # Get unitary system
    air_loop_hvac.supplyComponents.each do |comp|
      if comp.to_AirLoopHVACUnitarySystem.is_initialized
        unitary_system = comp.to_AirLoopHVACUnitarySystem.get
      end
    end
    return false if unitary_system.nil?

    # Set fan operating schedule during assumed occupant standby mode time to 0 so the fan can cycle
    new_sch = model_set_schedule_value(unitary_system.supplyAirFanOperatingModeSchedule.get, '12' => 0)
    unitary_system.setSupplyAirFanOperatingModeSchedule(new_sch) unless new_sch.nil?
  else
    # Get thermal zones associated with spaces having standby mode occupancy requirements
    standby_mode_zones = []
    standby_mode_spaces.sort.each do |space|
      standby_mode_zones << space.thermalZone.get
    end
    # Schedule the MDP of terminals to a low value during occupant standby mode
    # The intent is to reduce ventilation while still allowing the terminal to
    # meet loads
    standby_mode_zones.each do |zone|
      air_terminal = zone.airLoopHVACTerminal
      if air_terminal.is_initialized
        air_terminal = air_terminal.get
        if air_terminal.to_AirTerminalSingleDuctVAVReheat.is_initialized
          air_terminal = air_terminal.to_AirTerminalSingleDuctVAVReheat.get
          if air_terminal.zoneMinimumAirFlowInputMethod == 'Constant' || air_terminal.zoneMinimumAirFlowInputMethod == 'FixedFlow'
            if air_terminal.zoneMinimumAirFlowInputMethod == 'FixedFlow'
              mdp_org = air_terminal.fixedMinimumAirFlowRate.get / air_terminal.autosizedMaximumAirFlowRate.get
              air_terminal.setFixedMinimumAirFlowRate(0)
            else
              mdp_org = air_terminal.constantMinimumAirFlowFraction.get
              air_terminal.setConstantMinimumAirFlowFraction(0)
            end
            air_terminal.setZoneMinimumAirFlowInputMethod('Scheduled')
            air_terminal.setMinimumAirFlowFractionSchedule(model_set_schedule_value(OpenstudioStandards::Schedules.create_constant_schedule_ruleset(air_loop_hvac.model, mdp_org, name: "#{air_terminal.name} - MDP", schedule_type_limit: 'Fraction'), '12' => 0.1))
          elsif air_terminal.zoneMinimumAirFlowInputMethod == 'Scheduled'
            air_terminal.setMinimumAirFlowFractionSchedule(model_set_schedule_value(air_terminal.minimumAirFlowFractionSchedule.get, '12' => 0.1))
          else
            OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "The air terminal associated with #{zone.name} uses a zone minimum air flow input method that is currently not supported so occupant standby controls were not modeled.")
          end
        else
          OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.AirLoopHVAC', "The air terminal associated with #{zone.name} isn't of the SingleDuctVAVReheat type so occupant standby controls were not modeled.")
        end
      end
    end
  end

  return true
end

#air_loop_hvac_supply_air_temperature_reset_required?(air_loop_hvac, climate_zone) ⇒ Boolean

Determine if the system required supply air temperature (SAT) reset. For 90.1-2019, SAT reset requirements are based on climate zone. More exceptions are added for 90.1 2019 6.5.3.5

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    air loop

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Boolean)

    returns true if required, false if not



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 331

def air_loop_hvac_supply_air_temperature_reset_required?(air_loop_hvac, climate_zone)
  is_sat_reset_required = false

  # Only required for multizone VAV systems
  unless air_loop_hvac_multizone_vav_system?(air_loop_hvac)
    return is_sat_reset_required
  end

  # check if design outside air is less than 10,000cfm (5000L/s) 90.1 2019 6.5.3.5 Exception 1 and 2
  design_oa_m3s = nil
  if air_loop_hvac.sizingSystem.designOutdoorAirFlowRate.is_initialized
    design_oa_m3s = air_loop_hvac.sizingSystem.designOutdoorAirFlowRate.get
  elsif air_loop_hvac.sizingSystem.autosizedDesignOutdoorAirFlowRate.is_initialized
    design_oa_m3s = air_loop_hvac.sizingSystem.autosizedDesignOutdoorAirFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name} design outdoor air flow rate is not available.")
  end
  design_oa_cfm = OpenStudio.convert(design_oa_m3s, 'm^3/s', 'cfm').get

  # check if there is erv 90.1 2019 Exceptions to 6.5.3.5 Exception 3
  has_erv = air_loop_hvac_energy_recovery?(air_loop_hvac)
  design_sa_m3s = air_loop_hvac_find_design_supply_air_flow_rate(air_loop_hvac)

  oa_ratio = 0
  if design_sa_m3s > 0
    oa_ratio = design_oa_m3s / design_sa_m3s
  end
  has_large_oa = (oa_ratio >= 0.8)

  case climate_zone
  when 'ASHRAE 169-2006-0A',
       'ASHRAE 169-2006-1A',
       'ASHRAE 169-2006-3A',
       'ASHRAE 169-2013-0A',
       'ASHRAE 169-2013-1A',
       'ASHRAE 169-2013-3A'
    if design_oa_cfm < 3000
      is_sat_reset_required = false
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is not required per 6.5.3.5 Exception 1, the system is located in climate zone #{climate_zone}.")
      return is_sat_reset_required
    end
    if has_erv && has_large_oa
      is_sat_reset_required = false
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is not required per 6.5.3.5 Exception 3, the system is located in climate zone #{climate_zone}.")
      return is_sat_reset_required
    end
    is_sat_reset_required = true
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is required.")
    return is_sat_reset_required
  when 'ASHRAE 169-2006-2A',
       'ASHRAE 169-2013-2A'
    if design_oa_cfm < 10000
      is_sat_reset_required = false
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is not required per 6.5.3.5 Exception 2, the system is located in climate zone #{climate_zone}.")
      return is_sat_reset_required
    end
    if has_erv && has_large_oa
      is_sat_reset_required = false
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is not required per 6.5.3.5 Exception 3, the system is located in climate zone #{climate_zone}.")
      return is_sat_reset_required
    end
    is_sat_reset_required = true
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is required.")
    return is_sat_reset_required
  when 'ASHRAE 169-2006-0B',
       'ASHRAE 169-2006-1B',
       'ASHRAE 169-2006-2B',
       'ASHRAE 169-2006-3B',
       'ASHRAE 169-2006-3C',
       'ASHRAE 169-2006-4A',
       'ASHRAE 169-2006-4B',
       'ASHRAE 169-2006-4C',
       'ASHRAE 169-2006-5A',
       'ASHRAE 169-2006-5B',
       'ASHRAE 169-2006-5C',
       'ASHRAE 169-2006-6A',
       'ASHRAE 169-2006-6B',
       'ASHRAE 169-2006-7A',
       'ASHRAE 169-2006-7B',
       'ASHRAE 169-2006-8A',
       'ASHRAE 169-2006-8B',
       'ASHRAE 169-2013-0B',
       'ASHRAE 169-2013-1B',
       'ASHRAE 169-2013-2B',
       'ASHRAE 169-2013-3B',
       'ASHRAE 169-2013-3C',
       'ASHRAE 169-2013-4A',
       'ASHRAE 169-2013-4B',
       'ASHRAE 169-2013-4C',
       'ASHRAE 169-2013-5A',
       'ASHRAE 169-2013-5B',
       'ASHRAE 169-2013-5C',
       'ASHRAE 169-2013-6A',
       'ASHRAE 169-2013-6B',
       'ASHRAE 169-2013-7A',
       'ASHRAE 169-2013-7B',
       'ASHRAE 169-2013-8A',
       'ASHRAE 169-2013-8B'
    is_sat_reset_required = true
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Supply air temperature reset is required.")
    return is_sat_reset_required
  end
end

#air_terminal_single_duct_vav_reheat_apply_initial_prototype_damper_position(air_terminal_single_duct_vav_reheat, zone_oa_per_area) ⇒ Boolean

Set the initial minimum damper position based on OA rate of the space and the template. Zones with low OA per area get lower initial guesses. Final position will be adjusted upward as necessary by Standards.AirLoopHVAC.apply_minimum_vav_damper_positions

Parameters:

  • air_terminal_single_duct_vav_reheat (OpenStudio::Model::AirTerminalSingleDuctVAVReheat)

    the air terminal object

  • zone_oa_per_area (Double)

    the zone outdoor air per area in m^3/s*m^2

Returns:

  • (Boolean)

    returns true if successful, false if not



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb', line 10

def air_terminal_single_duct_vav_reheat_apply_initial_prototype_damper_position(air_terminal_single_duct_vav_reheat, zone_oa_per_area)
  min_damper_position = case air_terminal_single_duct_vav_reheat_reheat_type(air_terminal_single_duct_vav_reheat)
                        when 'Electricity', 'NaturalGas'
                          0.3
                        else # 'HotWater', other
                          0.2
                        end

  # Set the minimum flow fraction
  air_terminal_single_duct_vav_reheat.setConstantMinimumAirFlowFraction(min_damper_position)

  return true
end

#air_terminal_single_duct_vav_reheat_minimum_damper_position(air_terminal_single_duct_vav_reheat, has_ddc = false) ⇒ Double

Specifies the minimum damper position for VAV dampers. For terminals with hot water heat and DDC, the minimum is 20%, otherwise the minimum is 30%.

Parameters:

  • air_terminal_single_duct_vav_reheat (OpenStudio::Model::AirTerminalSingleDuctVAVReheat)

    the air terminal object

  • has_ddc (Boolean) (defaults to: false)

    whether or not there is DDC control of the VAV terminal in question

Returns:

  • (Double)

    minimum damper position



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb', line 10

def air_terminal_single_duct_vav_reheat_minimum_damper_position(air_terminal_single_duct_vav_reheat, has_ddc = false)
  min_damper_position = nil
  case air_terminal_single_duct_vav_reheat_reheat_type(air_terminal_single_duct_vav_reheat)
  when 'HotWater'
    min_damper_position = if has_ddc
                            0.2
                          else
                            0.3
                          end
  when 'Electricity', 'NaturalGas'
    min_damper_position = 0.3
  end

  return min_damper_position
end

#boiler_get_eff_fplr(boiler_hot_water) ⇒ String

Determine what part load efficiency degredation curve should be used for a boiler

Parameters:

  • boiler_hot_water (OpenStudio::Model::BoilerHotWater)

    hot water boiler object

Returns:

  • (String)

    returns name of the boiler curve to be used, or nil if not applicable



6
7
8
9
10
11
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.BoilerHotWater.rb', line 6

def boiler_get_eff_fplr(boiler_hot_water)
  capacity_w = boiler_hot_water_find_capacity(boiler_hot_water)
  capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
  fplr = capacity_btu_per_hr >= 1_000_000 ? 'Boiler with Minimum Turndown' : 'Boiler with No Minimum Turndown'
  return fplr
end

#chiller_electric_eir_get_cap_f_t_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path) ⇒ String

TODO:

the current assingment is meant to replicate what was in the data, it probably needs to be reviewed

Get applicable performance curve for capacity as a function of temperature

Parameters:

  • chiller_electric_eir (OpenStudio::Model::ChillerElectricEIR)

    chiller object

  • compressor_type (String)

    compressor type

  • cooling_type (String)

    cooling type (‘AirCooled’ or ‘WaterCooled’)

  • chiller_tonnage (Double)

    chiller capacity in ton

Returns:

  • (String)

    name of applicable cuvre, nil if not found



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ChillerElectricEIR.rb', line 10

def chiller_electric_eir_get_cap_f_t_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path)
  case cooling_type
  when 'AirCooled'
    return 'AirCooled_Chiller_2010_PathA_CAPFT'
  when 'WaterCooled'
    case compressor_type
    when 'Centrifugal'
      if compliance_path == 'Path A'
        return 'WaterCooled_Centrifugal_Chiller_2010_PathA_CAPFT'
      elsif compliance_path == 'Path B'
        return 'WaterCooled_Centrifugal_Chiller_2010_PathB_CAPFT'
      else
        return nil
      end
    when 'Reciprocating', 'Rotary Screw', 'Scroll'
      return 'ChlrWtrPosDispPathAAllQRatio_fTchwsTcwsSI'
    else
      return nil
    end
  else
    return nil
  end
end

#chiller_electric_eir_get_eir_f_plr_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path) ⇒ String

TODO:

the current assingment is meant to replicate what was in the data, it probably needs to be reviewed

Get applicable performance curve for EIR as a function of part load ratio

Parameters:

  • chiller_electric_eir (OpenStudio::Model::ChillerElectricEIR)

    chiller object

  • compressor_type (String)

    compressor type

  • cooling_type (String)

    cooling type (‘AirCooled’ or ‘WaterCooled’)

  • chiller_tonnage (Double)

    chiller capacity in ton

Returns:

  • (String)

    name of applicable cuvre, nil if not found



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ChillerElectricEIR.rb', line 74

def chiller_electric_eir_get_eir_f_plr_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path)
  case cooling_type
  when 'AirCooled'
    return 'AirCooled_Chiller_AllCapacities_2004_2010_EIRFPLR'
  when 'WaterCooled'
    case compressor_type
    when 'Centrifugal'
      return 'ChlrWtrCentPathAAllEIRRatio_fQRatio'
    when 'Reciprocating', 'Rotary Screw', 'Scroll'
      return 'ChlrWtrPosDispPathAAllEIRRatio_fQRatio'
    else
      return nil
    end
  else
    return nil
  end
end

#chiller_electric_eir_get_eir_f_t_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path) ⇒ String

TODO:

the current assingment is meant to replicate what was in the data, it probably needs to be reviewed

Get applicable performance curve for EIR as a function of temperature

Parameters:

  • chiller_electric_eir (OpenStudio::Model::ChillerElectricEIR)

    chiller object

  • compressor_type (String)

    compressor type

  • cooling_type (String)

    cooling type (‘AirCooled’ or ‘WaterCooled’)

  • chiller_tonnage (Double)

    chiller capacity in ton

Returns:

  • (String)

    name of applicable cuvre, nil if not found



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ChillerElectricEIR.rb', line 42

def chiller_electric_eir_get_eir_f_t_curve_name(chiller_electric_eir, compressor_type, cooling_type, chiller_tonnage, compliance_path)
  case cooling_type
  when 'AirCooled'
    return 'AirCooled_Chiller_2010_PathA_EIRFT'
  when 'WaterCooled'
    case compressor_type
    when 'Centrifugal'
      if compliance_path == 'Path A'
        return 'WaterCooled_Centrifugal_Chiller_2010_PathA_EIRFT'
      elsif compliance_path == 'Path B'
        return 'WaterCooled_Centrifugal_Chiller_2010_PathB_EIRFT'
      else
        return nil
      end
    when 'Reciprocating', 'Rotary Screw', 'Scroll'
      return 'ChlrWtrPosDispPathAAllEIRRatio_fTchwsTcwsSI'
    else
      return nil
    end
  else
    return nil
  end
end

#coil_heating_gas_additional_search_criteria(coil_heating_gas, search_criteria) ⇒ Hash

Applies the standard efficiency ratings to CoilHeatingGas.

Parameters:

  • coil_heating_gas (OpenStudio::Model::CoilHeatingGas)

    coil heating gas object

  • search_criteria (Hash)

    search criteria for looking up furnace data

Returns:

  • (Hash)

    updated search criteria



7
8
9
10
11
12
13
14
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoilHeatingGas.rb', line 7

def coil_heating_gas_additional_search_criteria(coil_heating_gas, search_criteria)
  capacity_w = coil_heating_gas_find_capacity(coil_heating_gas)
  capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
  if capacity_btu_per_hr < 225_000
    search_criteria['subtype'] = 'Weatherized' # assumption; could be based on input
  end
  return search_criteria
end

#cooling_tower_variable_speed_apply_efficiency_and_curves(cooling_tower_variable_speed) ⇒ Boolean

Apply the efficiency, plus Multicell heat rejection with VSD

Parameters:

  • cooling_tower_variable_speed (OpenStudio::Model::CoolingTowerVariableSpeed)

    variable speed cooling tower

Returns:

  • (Boolean)

    returns true if successful, false if not



10
11
12
13
14
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerVariableSpeed.rb', line 10

def cooling_tower_variable_speed_apply_efficiency_and_curves(cooling_tower_variable_speed)
  cooling_tower_apply_minimum_power_per_flow(cooling_tower_variable_speed)
  cooling_tower_variable_speed.setCellControl('MaximalCell')
  return true
end

#fan_constant_volume_airloop_fan_pressure_rise(fan_constant_volume) ⇒ Double

Determine the prototype fan pressure rise for a constant volume fan on an AirLoopHVAC based on system airflow. Defaults to the logic from ASHRAE 90.1-2004 prototypes.

Parameters:

  • fan_constant_volume (OpenStudio::Model::FanConstantVolume)

    constant volume fan object

Returns:

  • (Double)

    pressure rise in inches H20



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanConstantVolume.rb', line 9

def fan_constant_volume_airloop_fan_pressure_rise(fan_constant_volume)
  # Get the max flow rate from the fan.
  maximum_flow_rate_m3_per_s = nil
  if fan_constant_volume.maximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_constant_volume.maximumFlowRate.get
  elsif fan_constant_volume.autosizedMaximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_constant_volume.autosizedMaximumFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.prototype.FanConstantVolume', "For #{fan_constant_volume.name} max flow rate is not available, cannot apply prototype assumptions.")
    return false
  end

  # Convert max flow rate to cfm
  maximum_flow_rate_cfm = OpenStudio.convert(maximum_flow_rate_m3_per_s, 'm^3/s', 'cfm').get

  # Determine the pressure rise
  pressure_rise_in_h2o = if maximum_flow_rate_cfm < 7437
                           2.5
                         else # Over 7,437 cfm
                           4.09
                         end

  return pressure_rise_in_h2o
end

#fan_on_off_airloop_or_unitary_fan_pressure_rise(fan_on_off) ⇒ Double

Determine the prototype fan pressure rise for an on off fan on an AirLoopHVAC or inside a unitary system based on system airflow. Defaults to the logic from ASHRAE 90.1-2004 prototypes.

Parameters:

  • fan_on_off (OpenStudio::Model::FanOnOff)

    on off fan object

Returns:

  • (Double)

    pressure rise in inches H20



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanOnOff.rb', line 9

def fan_on_off_airloop_or_unitary_fan_pressure_rise(fan_on_off)
  # Get the max flow rate from the fan.
  maximum_flow_rate_m3_per_s = nil
  if fan_on_off.maximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_on_off.maximumFlowRate.get
  elsif fan_on_off.autosizedMaximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_on_off.autosizedMaximumFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.prototype.FanOnOff', "For #{fan_on_off.name} max flow rate is not available, cannot apply prototype assumptions.")
    return false
  end

  # Convert max flow rate to cfm
  maximum_flow_rate_cfm = OpenStudio.convert(maximum_flow_rate_m3_per_s, 'm^3/s', 'cfm').get

  # Determine the pressure rise
  pressure_rise_in_h2o = if maximum_flow_rate_cfm < 7437
                           2.5
                         else # Over 7,437 cfm
                           4.09
                         end

  return pressure_rise_in_h2o
end

#fan_variable_volume_airloop_fan_pressure_rise(fan_variable_volume) ⇒ Double

Determine the prototype fan pressure rise for a variable volume fan on an AirLoopHVAC based on system airflow. Defaults to the logic from ASHRAE 90.1-2004 prototypes.

Parameters:

  • fan_variable_volume (OpenStudio::Model::FanVariableVolume)

    variable volume fan object

Returns:

  • (Double)

    pressure rise in inches H20



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanVariableVolume.rb', line 9

def fan_variable_volume_airloop_fan_pressure_rise(fan_variable_volume)
  # Get the max flow rate from the fan.
  maximum_flow_rate_m3_per_s = nil
  if fan_variable_volume.maximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_variable_volume.maximumFlowRate.get
  elsif fan_variable_volume.autosizedMaximumFlowRate.is_initialized
    maximum_flow_rate_m3_per_s = fan_variable_volume.autosizedMaximumFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.prototype.FanVariableVolume', "For #{fan_variable_volume.name} max flow rate is not available, cannot apply prototype assumptions.")
    return false
  end

  # Convert max flow rate to cfm
  maximum_flow_rate_cfm = OpenStudio.convert(maximum_flow_rate_m3_per_s, 'm^3/s', 'cfm').get

  # Determine the pressure rise
  pressure_rise_in_h2o = if maximum_flow_rate_cfm < 4648
                           4.0
                         else # Over 7,437 cfm
                           5.58
                         end

  return pressure_rise_in_h2o
end

#fan_variable_volume_part_load_fan_power_limitation_capacity_limit(fan_variable_volume) ⇒ Double

The threhold capacity below which part load control is not required. Per 90.1-2019, table 6.5.3.2.1: the cooling capacity threshold is 75000 instead of 110000 as of 1/1/2014

Parameters:

  • fan_variable_volume (OpenStudio::Model::FanVariableVolume)

    variable volume fan object

Returns:

  • (Double)

    the limit, in Btu/hr. Return nil for no limit by default.



27
28
29
30
31
32
33
34
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanVariableVolume.rb', line 27

def fan_variable_volume_part_load_fan_power_limitation_capacity_limit(fan_variable_volume)
  cap_limit_btu_per_hr = case fan_variable_volume_cooling_system_type(fan_variable_volume)
                         when 'dx'
                           65_000
                         end

  return cap_limit_btu_per_hr
end

#fan_variable_volume_part_load_fan_power_limitation_hp_limit(fan_variable_volume) ⇒ Double

The threhold horsepower below which part load control is not required. Per 90.1-2019, table 6.5.3.2.1: the fan motor size for chiller-water and evaporative cooling is 0.25 hp as of 1/1/2014 instead of 5 hp

Parameters:

  • fan_variable_volume (OpenStudio::Model::FanVariableVolume)

    variable volume fan object

Returns:

  • (Double)

    the limit, in horsepower. Return nil for no limit by default.



10
11
12
13
14
15
16
17
18
19
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.FanVariableVolume.rb', line 10

def fan_variable_volume_part_load_fan_power_limitation_hp_limit(fan_variable_volume)
  hp_limit = case fan_variable_volume_cooling_system_type(fan_variable_volume)
             when 'dx'
               0.0
             when 'chw', 'evap'
               0.25
             end

  return hp_limit
end

#load_standards_database(data_directories = []) ⇒ Hash

Loads the openstudio standards dataset for this standard.

Parameters:

  • data_directories (Array<String>) (defaults to: [])

    array of file paths that contain standards data

Returns:

  • (Hash)

    a hash of standards data



18
19
20
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.rb', line 18

def load_standards_database(data_directories = [])
  super([__dir__] + data_directories)
end

#model_add_lights_shutoff(model) ⇒ Boolean

Note:

code_sections [90.1-2016_9.4.1.1.h/i]

Implement occupancy based lighting level threshold (0.02 W/sqft). This is only for ASHRAE 90.1 2016 onwards.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio Model

Returns:

  • (Boolean)

    returns true if successful, false if not

Author:

  • Xuechen (Jerry) Lei, PNNL



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb', line 237

def model_add_lights_shutoff(model)
  zones = model.getThermalZones
  num_zones = 0
  business_sch_name = prototype_input['business_schedule']
  return if business_sch_name.nil? # This is only for 10 prototypes that do not have continuous operation.

  # Add business schedule
  model_add_schedule(model, business_sch_name)

  # Add EMS object for business schedule variable
  business_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
  business_sensor.setKeyName(business_sch_name)
  business_sensor.setName('Business_Sensor')
  business_sensor_name = business_sensor.name.to_s

  space_types_affected = []
  zones.sort.each do |zone|
    spaces = zone.spaces
    if spaces.length != 1
      puts 'warning, there are more than one spaces in the zone, need to confirm the implementation'
    end
    space = spaces[0]
    space_lights = space.lights
    lights_defined_by_spacetype = false
    if space_lights.empty?
      space_lights = space.spaceType.get.lights
      lights_defined_by_spacetype = true
      space_types_affected << space.spaceType
    end
    space_people = space.people
    if space_people.empty?
      space_people = space.spaceType.get.people
    end

    # guard clause to skip space with no lights
    next if space_lights.empty?

    # if lights are defined at the space type level, clone each lights object and make it individual to the space
    new_space_lights = []
    if lights_defined_by_spacetype
      space_lights.each do |lights|
        new_lights = lights.clone.to_Lights.get
        new_lights.setName("#{space.name}-#{lights.name}")
        new_lights.setSpace(space)
        new_space_lights << new_lights
      end
      space_lights = new_space_lights
    end

    zone_name = zone.name.to_s
    next if zone_name =~ /data\s*center/i # skip data centers

    # EnergyPlus v9.4.0 / OpenStudio v3.1.0 variable name change from 'Zone Lights Electric Power' to 'Zone Lights Electricity Rate'
    # EnergyPlus v9.6.0 / OpenStudio v3.3.0 added Space objects, variable name change from 'Zone Lights Electricity Rate' to 'Space Lights Electricity Rate'
    # https://github.com/NREL/OpenStudio/pull/4104
    if model.version < OpenStudio::VersionString.new('3.1.0')
      light_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Lights Electric Power')
      key_name = zone_name
    elsif model.version < OpenStudio::VersionString.new('3.3.0')
      light_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Lights Electricity Rate')
      key_name = zone_name
    else
      light_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Space Lights Electricity Rate')
      key_name = space.name.to_s
    end
    light_sensor.setKeyName(key_name)
    light_sensor_name_ems = "#{ems_friendly_name(key_name)}_LSr"
    light_sensor.setName(light_sensor_name_ems)

    # get the space floor area for calculations
    space_floor_area = space.floorArea

    # account for multiple lights (also work for single light)
    big_light = space_lights[0] # find the light with highest power (assuming specified by watts/area)
    space_lights.each do |light_x|
      big_light_power = big_light.definition.to_LightsDefinition.get.wattsperSpaceFloorArea.to_f
      light_x_power = light_x.definition.to_LightsDefinition.get.wattsperSpaceFloorArea.to_f
      if light_x_power > big_light_power
        big_light = light_x
      end
    end

    add_lights_prog_0 = ''
    add_lights_prog_null = ''
    light_id = 0
    space_lights.each do |light_x|
      light_id += 1
      # EnergyPlus v9.4 name change for EMS actuators
      # https://github.com/NREL/OpenStudio/pull/4104
      if model.version < OpenStudio::VersionString.new('3.1.0')
        light_x_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(light_x, 'Lights', 'Electric Power Level')
      else
        light_x_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(light_x, 'Lights', 'Electricity Rate')
      end
      light_x_actuator_name_ems = "#{ems_friendly_name(key_name)}_Light#{light_id}_Actuator"
      light_x_actuator.setName(light_x_actuator_name_ems)
      add_lights_prog_null += "\n      SET #{light_x_actuator_name_ems} = NULL,"
      if light_x == big_light
        add_lights_prog_0 += "\n      SET #{light_x_actuator_name_ems} = 0.02*#{space_floor_area}/0.09290304,"
        next
      end
      add_lights_prog_0 += "\n      SET #{light_x_actuator_name_ems} = 0,"
    end

    light_ems_prog = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
    light_ems_prog.setName("SET_#{ems_friendly_name(key_name)}_Light_EMS_Program")
    light_ems_prog_body = <<-EMS
    SET #{light_sensor_name_ems}_IP=0.093*#{light_sensor_name_ems}/#{space_floor_area},
    IF (#{business_sensor_name} <= 0) && (#{light_sensor_name_ems}_IP >= 0.02),#{add_lights_prog_0}
    ELSE,#{add_lights_prog_null}
    ENDIF
    EMS
    light_ems_prog.setBody(light_ems_prog_body)

    light_ems_prog_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
    light_ems_prog_manager.setName("SET_#{ems_friendly_name(key_name)}_Light_EMS_Program_Manager")
    light_ems_prog_manager.setCallingPoint('AfterPredictorAfterHVACManagers')
    light_ems_prog_manager.addProgram(light_ems_prog)
  end

  # remove lights at the space type level
  space_types_affected.each do |space_type|
    space_type.get.lights.each(&:remove)
  end

  return true
end

#model_cw_loop_cooling_tower_fan_type(model) ⇒ String

Determine which type of fan the cooling tower will have. Variable Speed Fan for ASHRAE 90.1-2019.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

Returns:

  • (String)

    the fan type: TwoSpeed Fan, Variable Speed Fan



9
10
11
12
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb', line 9

def model_cw_loop_cooling_tower_fan_type(model)
  fan_type = 'Variable Speed Fan'
  return fan_type
end

#model_door_infil_flow_rate_metal_coiling_cfm_ft2(climate_zone) ⇒ Double

Note:

code_sections [90.1-2019_5.4.3.2]

Metal coiling door code minimum infiltration rate at 75 Pa

Parameters:

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Double)

    Minimum infiltration rate for metal coiling doors



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb', line 215

def model_door_infil_flow_rate_metal_coiling_cfm_ft2(climate_zone)
  case climate_zone
    when 'ASHRAE 169-2006-7A',
         'ASHRAE 169-2006-7B',
         'ASHRAE 169-2006-8A',
         'ASHRAE 169-2006-8B',
         'ASHRAE 169-2013-7A',
         'ASHRAE 169-2013-7B',
         'ASHRAE 169-2013-8A',
         'ASHRAE 169-2013-8B'
      return 0.4
    else
      return 1.0
  end
end

#model_economizer_type(model, climate_zone) ⇒ String

Determine the prototypical economizer type for the model.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (String)

    the economizer type. Possible values are: ‘NoEconomizer’ ‘FixedDryBulb’ ‘FixedEnthalpy’ ‘DifferentialDryBulb’ ‘DifferentialEnthalpy’ ‘FixedDewPointAndDryBulb’ ‘ElectronicEnthalpy’ ‘DifferentialDryBulbAndEnthalpy’



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb', line 17

def model_economizer_type(model, climate_zone)
  economizer_type = case climate_zone
                    when 'ASHRAE 169-2006-0A',
                        'ASHRAE 169-2006-1A',
                        'ASHRAE 169-2006-2A',
                        'ASHRAE 169-2006-3A',
                        'ASHRAE 169-2006-4A',
                        'ASHRAE 169-2013-0A',
                        'ASHRAE 169-2013-1A',
                        'ASHRAE 169-2013-2A',
                        'ASHRAE 169-2013-3A',
                        'ASHRAE 169-2013-4A'
                      'DifferentialEnthalpy'
                    else
                      'DifferentialDryBulb'
                    end
  return economizer_type
end

#model_elevator_fan_pwr(model, vent_rate_cfm) ⇒ Double

Determines the power of the elevator ventilation fan. 90.1-2019 has a requirement for ventilation fan efficiency.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • vent_rate_cfm (Double)

    the ventilation rate in ft^3/min

Returns:

  • (Double)

    the ventilation fan power in watts



21
22
23
24
25
26
27
28
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.elevators.rb', line 21

def model_elevator_fan_pwr(model, vent_rate_cfm)
  vent_pwr_per_flow_w_per_cfm = 0.33
  vent_pwr_w = vent_pwr_per_flow_w_per_cfm * vent_rate_cfm
  # addendum 90.1-2007 aj has requirement on efficiency
  vent_pwr_w = vent_pwr_w * 0.29 / 0.70

  return vent_pwr_w
end

#model_elevator_lighting_pct_incandescent(model) ⇒ Double

Determines the percentage of the elevator cab lighting that is incandescent. The remainder is assumed to be LED. Defaults to 0% incandescent (100% LED), representing newer elevators.

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

Returns:

  • (Double)

    incandescent lighting percentage



10
11
12
13
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.elevators.rb', line 10

def model_elevator_lighting_pct_incandescent(model)
  pct_incandescent = 0.0 # 100% LED
  return pct_incandescent
end

#model_fenestration_orientation(model, climate_zone) ⇒ Boolean

Note:

code_sections [90.1-2013_5.5.4.5]

Adjust model to comply with fenestration orientation requirements

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • climate_zone (String)

    ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’

Returns:

  • (Boolean)

    Returns true if successful, false otherwise



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb', line 10

def model_fenestration_orientation(model, climate_zone)
  # Building rotation to meet the same code requirement for
  # 90.1-2010 are kept
  if model.getBuilding.standardsBuildingType.is_initialized
    building_type = model.getBuilding.standardsBuildingType.get

    case building_type
      when 'Hospital'
        # Rotate the building counter-clockwise
        OpenstudioStandards::Geometry.model_set_building_north_axis(model, 270.0)
      when 'SmallHotel'
        # Rotate the building clockwise
        OpenstudioStandards::Geometry.model_set_building_north_axis(model, 180)
    end
  end

  wwr = false
  # Section 6.2.1.2 in the ANSI/ASHRAE/IES Standard 90.1-2013 Determination
  # of Energy Savings: Quantitative Analysis mentions that the SHGC trade-off
  # path is most likely to be used by designers for compliance.
  #
  # The following adjustment are only made for models with simple glazing objects
  non_simple_glazing = false
  shgc_a = 0
  model.getSpaces.each do |space|
    # Get thermal zone multiplier
    multiplier = space.thermalZone.get.multiplier

    space.surfaces.each do |surface|
      surface.subSurfaces.each do |subsurface|
        # Get window subsurface type
        subsurface_type = subsurface.subSurfaceType.to_s.downcase

        # Window, glass doors
        next unless (subsurface_type.include? 'window') || (subsurface_type.include? 'glass')

        # Check if non simple glazing fenestration objects are used
        subsurface_cons = subsurface.construction.get.to_Construction.get
        non_simple_glazing = true unless subsurface_cons.layers[0].to_SimpleGlazing.is_initialized

        if non_simple_glazing
          OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2013.model', 'Fenestration objects in the model use non-simple glazing models, fenestration requirements are not applied')
          return false
        end

        # Get subsurface's simple glazing object
        subsurface_shgc = subsurface_cons.layers[0].to_SimpleGlazing.get.solarHeatGainCoefficient

        # Get subsurface area
        subsurface_area = subsurface.grossArea * subsurface.multiplier * multiplier

        # SHGC * Area
        shgc_a += subsurface_shgc * subsurface_area
      end
    end
  end

  # Calculate West, East and total fenestration area
  a_n = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)['north_window']
  a_s = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)['south_window']
  a_e = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)['east_window']
  a_w = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)['west_window']
  a_t = a_n + a_s + a_e + a_w

  return true if a_t.abs < 0.01

  # For prototypes SHGC_c assumed to be the building's weighted average SHGC
  shgc_c = shgc_a / a_t
  shgc_c = shgc_c.round(2)

  # West and East facing WWR
  wwr_w = OpenstudioStandards::Geometry.model_get_exterior_window_to_wall_ratio(model, cardinal_direction: 'W')
  wwr_e = OpenstudioStandards::Geometry.model_get_exterior_window_to_wall_ratio(model, cardinal_direction: 'E')

  # Calculate new SHGC for west and east facing fenestration;
  # Create new simple glazing object and assign it to all
  # West and East fenestration
  #
  # Exception 5 is applied when applicable
  shgc_w = 0
  shgc_e = 0

  # Determine requirement criteria
  case climate_zone
    when 'ASHRAE 169-2006-0A',
         'ASHRAE 169-2006-0B',
         'ASHRAE 169-2006-1A',
         'ASHRAE 169-2006-1B',
         'ASHRAE 169-2006-2A',
         'ASHRAE 169-2006-2B',
         'ASHRAE 169-2006-3A',
         'ASHRAE 169-2006-3B',
         'ASHRAE 169-2006-3C',
         'ASHRAE 169-2013-0A',
         'ASHRAE 169-2013-0B',
         'ASHRAE 169-2013-1A',
         'ASHRAE 169-2013-1B',
         'ASHRAE 169-2013-2A',
         'ASHRAE 169-2013-2B',
         'ASHRAE 169-2013-3A',
         'ASHRAE 169-2013-3B',
         'ASHRAE 169-2013-3C'
      criteria = 4
    when 'ASHRAE 169-2006-4A',
         'ASHRAE 169-2006-4B',
         'ASHRAE 169-2006-4C',
         'ASHRAE 169-2006-5A',
         'ASHRAE 169-2006-5B',
         'ASHRAE 169-2006-5C',
         'ASHRAE 169-2006-6A',
         'ASHRAE 169-2006-6B',
         'ASHRAE 169-2006-7A',
         'ASHRAE 169-2006-7B',
         'ASHRAE 169-2006-8A',
         'ASHRAE 169-2006-8B',
         'ASHRAE 169-2013-4A',
         'ASHRAE 169-2013-4B',
         'ASHRAE 169-2013-4C',
         'ASHRAE 169-2013-5A',
         'ASHRAE 169-2013-5B',
         'ASHRAE 169-2013-5C',
         'ASHRAE 169-2013-6A',
         'ASHRAE 169-2013-6B',
         'ASHRAE 169-2013-7A',
         'ASHRAE 169-2013-7B',
         'ASHRAE 169-2013-8A',
         'ASHRAE 169-2013-8B'
      criteria = 5
    else
      return false
  end

  if !((a_w <= a_t / criteria) && (a_e <= a_t / criteria))
    # Calculate new SHGC
    if wwr_w > 0.2
      shgc_w = a_t * shgc_c / (criteria * a_w)
    end
    if wwr_e > 0.2
      shgc_e = a_t * shgc_c / (criteria * a_w)
    end

    # No SHGC adjustment needed
    return true if shgc_w == 0 && shgc_e == 0

    model.getSpaces.each do |space|
      # Get thermal zone multiplier
      multiplier = space.thermalZone.get.multiplier

      space.surfaces.each do |surface|
        # Proceed only for East and West facing surfaces that are required
        # to have their SHGC adjusted
        next unless (OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'W' && shgc_w > 0) ||
                    (OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'E' && shgc_e > 0)

        surface.subSurfaces.each do |subsurface|
          # Get window subsurface type
          subsurface_type = subsurface.subSurfaceType.to_s.downcase

          # Window, glass doors
          next unless (subsurface_type.include? 'window') || (subsurface_type.include? 'glass')

          new_shgc = OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'W' ? shgc_w : shgc_e
          new_shgc = new_shgc.round(2)

          # Get construction/simple glazing associated with the subsurface
          subsurface_org_cons = subsurface.construction.get.to_Construction.get
          subsurface_org_cons_mat = subsurface_org_cons.layers[0].to_SimpleGlazing.get

          # Only proceed if new SHGC is different than orignal one
          next unless (new_shgc - subsurface_org_cons_mat.solarHeatGainCoefficient).abs > 0

          # Clone construction/simple glazing associated with the subsurface
          subsurface_new_cons = subsurface_org_cons.clone(model).to_Construction.get
          subsurface_new_cons.setName("#{subsurface.name} Wind Cons U-#{OpenStudio.convert(subsurface_org_cons_mat.uFactor, 'W/m^2*K', 'Btu/ft^2*h*R').get.round(2)} SHGC #{new_shgc}")
          subsurface_new_cons_mat = subsurface_org_cons_mat.clone(model).to_SimpleGlazing.get
          subsurface_new_cons_mat.setName("#{subsurface.name} Wind SG Mat U-#{OpenStudio.convert(subsurface_org_cons_mat.uFactor, 'W/m^2*K', 'Btu/ft^2*h*R').get.round(2)} SHGC #{new_shgc}")
          subsurface_new_cons_mat.setSolarHeatGainCoefficient(new_shgc)
          new_layers = OpenStudio::Model::MaterialVector.new
          new_layers << subsurface_new_cons_mat
          subsurface_new_cons.setLayers(new_layers)

          # Assign new construction to sub surface
          subsurface.setConstruction(subsurface_new_cons)
        end
      end
    end
  end

  return true
end

#model_transfer_air_required?(model) ⇒ Boolean

Note:

code_sections [90.1-2019_6.5.7.1]

Is transfer air required?

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

Returns:

  • (Boolean)

    returns true if transfer air is required, false if not



206
207
208
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb', line 206

def model_transfer_air_required?(model)
  return true
end

#pump_standard_minimum_motor_efficiency_and_size(pump, motor_bhp) ⇒ Array<Double>

Determines the minimum pump motor efficiency and nominal size for a given motor bhp. This should be the total brake horsepower with any desired safety factor already included. This method picks the next nominal motor catgory larger than the required brake horsepower, and the efficiency is based on that size. For example, if the bhp = 6.3, the nominal size will be 7.5HP and the efficiency for 90.1-2010 will be 91.7% from Table 10.8B. This method assumes 4-pole, 1800rpm totally-enclosed fan-cooled motors.

Parameters:

  • motor_bhp (Double)

    motor brake horsepower (hp)

Returns:

  • (Array<Double>)

    minimum motor efficiency (0.0 to 1.0), nominal horsepower



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Pump.rb', line 15

def pump_standard_minimum_motor_efficiency_and_size(pump, motor_bhp)
  motor_eff = 0.85
  nominal_hp = motor_bhp

  # Don't attempt to look up motor efficiency
  # for zero-hp pumps (required for circulation-pump-free
  # service water heating systems).
  return [1.0, 0] if motor_bhp < 0.0001

  # Lookup the minimum motor efficiency
  motors = standards_data['motors']

  # Assuming all pump motors are 4-pole ODP
  search_criteria = {
    'template' => template,
    'number_of_poles' => 4.0,
    'type' => 'Enclosed'
  }

  motor_properties = model_find_object(motors, search_criteria, motor_bhp)
  if motor_properties.nil?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Pump', "For #{pump.name}, could not find motor properties using search criteria: #{search_criteria}, motor_bhp = #{motor_bhp} hp.")
    return [motor_eff, nominal_hp]
  end

  motor_eff = motor_properties['nominal_full_load_efficiency']
  nominal_hp = motor_properties['maximum_capacity'].to_f.round(1)
  # Round to nearest whole HP for niceness
  if nominal_hp >= 2
    nominal_hp = nominal_hp.round
  end

  # Get the efficiency based on the nominal horsepower
  # Add 0.01 hp to avoid search errors.
  motor_properties = model_find_object(motors, search_criteria, nominal_hp + 0.01)
  if motor_properties.nil?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Fan', "For #{pump.name}, could not find nominal motor properties using search criteria: #{search_criteria}, motor_hp = #{nominal_hp} hp.")
    return [motor_eff, nominal_hp]
  end
  motor_eff = motor_properties['nominal_full_load_efficiency']

  # Get flow rate (whether autosized or hard-sized)
  flow_m3_per_s = 0
  flow_m3_per_s = if pump.to_PumpVariableSpeed.is_initialized || pump.to_PumpConstantSpeed.is_initialized
                    if pump.ratedFlowRate.is_initialized
                      pump.ratedFlowRate.get
                    elsif pump.autosizedRatedFlowRate.is_initialized
                      pump.autosizedRatedFlowRate.get
                    end
                  elsif pump.to_HeaderedPumpsVariableSpeed.is_initialized || pump.to_HeaderedPumpsConstantSpeed.is_initialized
                    if pump.totalRatedFlowRate.is_initialized
                      pump.totalRatedFlowRate.get / pump.numberofPumpsinBank
                    elsif pump.autosizedTotalRatedFlowRate.is_initialized
                      pump.autosizedTotalRatedFlowRate.get / pump.numberofPumpsinBank
                    end
                  end
  flow_gpm = OpenStudio.convert(flow_m3_per_s, 'm^3/s', 'gal/min').get

  # Adjustment for clean water pumps requirement:
  # The adjustment is made based on results included
  # in https://www.energy.gov/sites/prod/files/2015/12/f28/Pumps%20ECS%20Final%20Rule.pdf
  # Table 1 summarizes final rule efficiency levels
  # analyzed with corresponding C-values. With the
  # rulemaking adopted TSL/EL2 from the report, it shows
  # about 4.3% of average efficiency improvement, and after
  # considering 25% of the market, about 1.1% of the
  # final average efficiency improvement is estimated.
  #
  # The clean water pump requirement is only
  # applied to pumps with a flow rate of at least 25 gpm
  motor_eff *= 1.011 unless flow_gpm < 25.0

  return [motor_eff, nominal_hp]
end

#pump_variable_speed_get_control_type(pump, plant_loop_type, pump_nominal_hp) ⇒ String

Note:

code_sections [90.1-2019_6.5.4.2]

Determine type of pump part load control type

Parameters:

  • pump (OpenStudio::Model::PumpVariableSpeed)

    OpenStudio pump object

  • plant_loop_type (String)

    Type of plant loop

  • pump_nominal_hp (Float)

    Pump nominal horsepower

Returns:

  • (String)

    Pump part load control type



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb', line 11

def pump_variable_speed_get_control_type(pump, plant_loop_type, pump_nominal_hp)
  # Sizing factor to take into account that pumps
  # are typically sized to handle a ~10% pressure
  # increase and ~10% flow increase.
  design_sizing_factor = 1.25

  # Get climate zone
  climate_zone = pump.plantLoop.get.model.getClimateZones.getClimateZone(0)
  climate_zone = "#{climate_zone.institution} 169-#{climate_zone.year}-#{climate_zone.value}"

  # Get nameplate hp threshold:
  # The thresholds below represent the nameplate
  # hp one level lower than the threshold in the
  # code. Motor size from table in section 10 are
  # used as reference.
  case plant_loop_type
    when 'Heating'
      case climate_zone
        when 'ASHRAE 169-2006-7A',
             'ASHRAE 169-2006-7B',
             'ASHRAE 169-2006-8A',
             'ASHRAE 169-2006-8B',
             'ASHRAE 169-2013-7A',
             'ASHRAE 169-2013-7B',
             'ASHRAE 169-2013-8A',
             'ASHRAE 169-2013-8B'
          threshold = 3
        when 'ASHRAE 169-2006-3C',
             'ASHRAE 169-2006-5A',
             'ASHRAE 169-2006-5C',
             'ASHRAE 169-2006-6A',
             'ASHRAE 169-2006-6B',
             'ASHRAE 169-2013-3C',
             'ASHRAE 169-2013-5A',
             'ASHRAE 169-2013-5C',
             'ASHRAE 169-2013-6A',
             'ASHRAE 169-2013-6B'
          threshold = 5
        when 'ASHRAE 169-2006-4A',
             'ASHRAE 169-2006-4C',
             'ASHRAE 169-2006-5B',
             'ASHRAE 169-2013-4A',
             'ASHRAE 169-2013-4C',
             'ASHRAE 169-2013-5B'
          threshold = 7.5
        when 'ASHRAE 169-2006-4B',
             'ASHRAE 169-2013-4B'
          threshold = 10
        when 'ASHRAE 169-2006-2A',
             'ASHRAE 169-2006-2B',
             'ASHRAE 169-2006-3A',
             'ASHRAE 169-2006-3B',
             'ASHRAE 169-2013-2A',
             'ASHRAE 169-2013-2B',
             'ASHRAE 169-2013-3A',
             'ASHRAE 169-2013-3B'
          threshold = 20
        when 'ASHRAE 169-2006-1B',
             'ASHRAE 169-2013-1B'
          threshold = 75
        when 'ASHRAE 169-2006-0A',
             'ASHRAE 169-2006-0B',
             'ASHRAE 169-2006-1A',
             'ASHRAE 169-2013-0A',
             'ASHRAE 169-2013-0B',
             'ASHRAE 169-2013-1A'
          threshold = 150
        else
          OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.PumpVariableSpeed', "Pump flow control requirement missing for heating water pumps in climate zone: #{climate_zone}.")
      end
    when 'Cooling'
      case climate_zone
        when 'ASHRAE 169-2006-0A',
             'ASHRAE 169-2006-0B',
             'ASHRAE 169-2006-1A',
             'ASHRAE 169-2006-1B',
             'ASHRAE 169-2006-2B',
             'ASHRAE 169-2013-0A',
             'ASHRAE 169-2013-0B',
             'ASHRAE 169-2013-1A',
             'ASHRAE 169-2013-1B',
             'ASHRAE 169-2013-2B'
          threshold = 1.5
        when 'ASHRAE 169-2006-2A',
             'ASHRAE 169-2006-3B',
             'ASHRAE 169-2013-2A',
             'ASHRAE 169-2013-3B'
          threshold = 2
        when 'ASHRAE 169-2006-3A',
             'ASHRAE 169-2006-3C',
             'ASHRAE 169-2006-4A',
             'ASHRAE 169-2006-4B',
             'ASHRAE 169-2013-3A',
             'ASHRAE 169-2013-3C',
             'ASHRAE 169-2013-4A',
             'ASHRAE 169-2013-4B'
          threshold = 3
        when 'ASHRAE 169-2006-4C',
             'ASHRAE 169-2006-5A',
             'ASHRAE 169-2006-5B',
             'ASHRAE 169-2006-5C',
             'ASHRAE 169-2006-6A',
             'ASHRAE 169-2006-6B',
             'ASHRAE 169-2013-4C',
             'ASHRAE 169-2013-5A',
             'ASHRAE 169-2013-5B',
             'ASHRAE 169-2013-5C',
             'ASHRAE 169-2013-6A',
             'ASHRAE 169-2013-6B'
          threshold = 5
        when 'ASHRAE 169-2006-7A',
             'ASHRAE 169-2006-7B',
             'ASHRAE 169-2006-8A',
             'ASHRAE 169-2006-8B',
             'ASHRAE 169-2013-7A',
             'ASHRAE 169-2013-7B',
             'ASHRAE 169-2013-8A',
             'ASHRAE 169-2013-8B'
          threshold = 10
        else
          OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.PumpVariableSpeed', "Pump flow control requirement missing for chilled water pumps in climate zone: #{climate_zone}.")
      end
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_2019.PumpVariableSpeed', "No pump flow requirement for #{plant_loop_type} plant loops.")
      return false
  end

  return 'VSD DP Reset' if pump_nominal_hp * design_sizing_factor > threshold

  # else
  return 'Riding Curve'
end

#set_maximum_fraction_outdoor_air_schedule(air_loop_hvac, oa_control, snc) ⇒ OpenStudio::Model::ScheduleRuleset

Create an economizer maximum OA fraction schedule with For ASHRAE 90.1 2019, a maximum of 75% to reflect damper leakage per PNNL

Parameters:

  • air_loop_hvac (OpenStudio::Model::AirLoopHVAC)

    HVAC air loop object

  • oa_control (OpenStudio::Model::ControllerOutdoorAir)

    Outdoor air controller object to have this maximum OA fraction schedule

  • snc (String)

    System name

Returns:

  • (OpenStudio::Model::ScheduleRuleset)

    Generated maximum outdoor air fraction schedule for later use



22
23
24
25
26
27
28
29
30
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb', line 22

def set_maximum_fraction_outdoor_air_schedule(air_loop_hvac, oa_control, snc)
  max_oa_sch_name = "#{snc}_maxOASch"
  max_oa_sch = OpenStudio::Model::ScheduleRuleset.new(air_loop_hvac.model)
  max_oa_sch.setName(max_oa_sch_name)
  max_oa_sch.defaultDaySchedule.setName("#{max_oa_sch_name}_Default")
  max_oa_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.75)
  oa_control.setMaximumFractionofOutdoorAirSchedule(max_oa_sch)
  max_oa_sch
end

#space_daylighted_area_window_width(space) ⇒ String

Determines the method used to extend the daylighted area horizontally next to a window. If the method is ‘fixed’, 2 ft is added to the width of each window. If the method is ‘proportional’, a distance equal to half of the head height of the window is added. If the method is ‘none’, no additional width is added.

Returns:

  • (String)

    returns ‘fixed’ or ‘proportional’



11
12
13
14
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 11

def space_daylighted_area_window_width(space)
  method = 'proportional'
  return method
end

#space_daylighting_control_required?(space, areas) ⇒ Array<Bool>

Determine if the space requires daylighting controls for toplighting, primary sidelighting, and secondary sidelighting. Defaults to false for all types.

Parameters:

  • space (OpenStudio::Model::Space)

    the space in question

  • areas (Hash)

    a hash of daylighted areas

Returns:

  • (Array<Bool>)

    req_top_ctrl, req_pri_ctrl, req_sec_ctrl



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 23

def space_daylighting_control_required?(space, areas)
  req_top_ctrl = true
  req_pri_ctrl = true
  req_sec_ctrl = true

  # Get the LPD of the space
  space_lpd_w_per_m2 = space.lightingPowerPerFloorArea

  # Primary Sidelighting
  # Check if the primary sidelit area contains less than 150W of lighting
  if areas['primary_sidelighted_area'] < 0.01
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.model.Space', "For #{space.name}, primary sidelighting control not required because primary sidelighted area = 0ft2 per 9.4.1.1(e).")
    req_pri_ctrl = false
  elsif areas['primary_sidelighted_area'] * space_lpd_w_per_m2 < 150.0
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Space', "For #{space.name}, primary sidelighting control not required because less than 150W of lighting are present in the primary daylighted area per 9.4.1.1(e).")
    req_pri_ctrl = false
  else
    # Check the size of the windows
    if areas['total_window_area'] < OpenStudio.convert(20.0, 'ft^2', 'm^2').get
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Space', "For #{space.name}, primary sidelighting control not required because there are less than 20ft2 of window per 9.4.1.1(e) Exception 2.")
      req_pri_ctrl = false
    end
  end

  # Secondary Sidelighting
  # Check if the primary and secondary sidelit areas contains less than 300W of lighting
  if areas['secondary_sidelighted_area'] < 0.01
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.model.Space', "For #{space.name}, secondary sidelighting control not required because secondary sidelighted area = 0ft2 per 9.4.1.1(e).")
    req_sec_ctrl = false
  elsif (areas['primary_sidelighted_area'] + areas['secondary_sidelighted_area']) * space_lpd_w_per_m2 < 300
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Space', "For #{space.name}, secondary sidelighting control not required because less than 300W of lighting are present in the combined primary and secondary daylighted areas per 9.4.1.1(e).")
    req_sec_ctrl = false
  else
    # Check the size of the windows
    if areas['total_window_area'] < OpenStudio.convert(20.0, 'ft^2', 'm^2').get
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Space', "For #{space.name}, secondary sidelighting control not required because there are less than 20ft2 of window per 9.4.1.1(e) Exception 2.")
      req_sec_ctrl = false
    end
  end

  # Toplighting
  # Check if the toplit area contains less than 150W of lighting
  if areas['toplighted_area'] < 0.01
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.model.Space', "For #{space.name}, toplighting control not required because toplighted area = 0ft2 per 9.4.1.1(f).")
    req_top_ctrl = false
  elsif areas['toplighted_area'] * space_lpd_w_per_m2 < 150
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Space', "For #{space.name}, toplighting control not required because less than 150W of lighting are present in the toplighted area per 9.4.1.1(f).")
    req_top_ctrl = false
  end

  # Exceptions
  if space.spaceType.is_initialized
    case space.spaceType.get.standardsSpaceType.to_s
    when 'Core_Retail'
      # Retail spaces exception (c) to Section 9.4.1.4
      # req_sec_ctrl set to true to create a second reference point
      req_pri_ctrl = false
      req_sec_ctrl = true
    when 'Entry', 'Front_Retail', 'Point_of_Sale', 'Strip mall - type 1', 'Strip mall - type 2', 'Strip mall - type 3'
      # Retail, Strip mall
      req_pri_ctrl = false
      req_sec_ctrl = false
    when 'Apartment', 'Apartment_topfloor_NS', 'Apartment_topfloor_WE'
      # Residential apartments
      req_top_ctrl = false
      req_pri_ctrl = false
      req_sec_ctrl = false
    end
  end

  return [req_top_ctrl, req_pri_ctrl, req_sec_ctrl]
end

#space_daylighting_control_type(space) ⇒ Object

Provide the type of daylighting control type

return [String] daylighting control type

Parameters:

  • space (OpenStudio::Model::Space)

    OpenStudio Space object



194
195
196
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 194

def space_daylighting_control_type(space)
  return 'ContinuousOff'
end

#space_daylighting_fractions_and_windows(space, areas, sorted_windows, sorted_skylights, req_top_ctrl, req_pri_ctrl, req_sec_ctrl) ⇒ Array

Determine the fraction controlled by each sensor and which window each sensor should go near.

Parameters:

  • space (OpenStudio::Model::Space)

    space object

  • areas (Hash)

    a hash of daylighted areas

  • sorted_windows (Hash)

    a hash of windows, sorted by priority

  • sorted_skylights (Hash)

    a hash of skylights, sorted by priority

  • req_top_ctrl (Boolean)

    if toplighting controls are required

  • req_pri_ctrl (Boolean)

    if primary sidelighting controls are required

  • req_sec_ctrl (Boolean)

    if secondary sidelighting controls are required

Returns:

  • (Array)

    array of 4 items

    sensor 1 fraction, sensor 2 fraction, sensor 1 window, sensor 2 window


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 107

def space_daylighting_fractions_and_windows(space,
                                            areas,
                                            sorted_windows,
                                            sorted_skylights,
                                            req_top_ctrl,
                                            req_pri_ctrl,
                                            req_sec_ctrl)
  sensor_1_frac = 0.0
  sensor_2_frac = 0.0
  sensor_1_window = nil
  sensor_2_window = nil

  # Get the area of the space
  space_area_m2 = space.floorArea

  # get the climate zone
  climate_zone = OpenstudioStandards::Weather.model_get_climate_zone(space.model)

  if req_top_ctrl && req_pri_ctrl && req_sec_ctrl
    # Sensor 1 controls toplighted area
    sensor_1_frac = areas['toplighted_area'] / space_area_m2
    sensor_1_window = sorted_skylights[0]
    # Sensor 2 controls primary + secondary area
    sensor_2_frac = (areas['primary_sidelighted_area'] + areas['secondary_sidelighted_area']) / space_area_m2
    sensor_2_window = sorted_windows[0]
  elsif !req_top_ctrl && req_pri_ctrl && req_sec_ctrl
    # Sensor 1 controls primary area
    sensor_1_frac = areas['primary_sidelighted_area'] / space_area_m2
    sensor_1_window = sorted_windows[0]
    # Sensor 2 controls secondary area
    sensor_2_frac = (areas['secondary_sidelighted_area'] / space_area_m2)
    sensor_2_window = sorted_windows[0]
  elsif req_top_ctrl && !req_pri_ctrl && req_sec_ctrl
    # Sensor 1 controls toplighted area
    sensor_1_frac = areas['toplighted_area'] / space_area_m2
    sensor_1_window = sorted_skylights[0]
    # Sensor 2 controls secondary area
    sensor_2_frac = (areas['secondary_sidelighted_area'] / space_area_m2)
    # sorted_skylights[0] assigned to sensor_2_window so a second reference point is added for top daylighting
    sensor_2_window = sorted_skylights[0]
  elsif req_top_ctrl && !req_pri_ctrl && !req_sec_ctrl
    case climate_zone
      when 'ASHRAE 169-2006-6A',
           'ASHRAE 169-2006-6B',
           'ASHRAE 169-2006-7A',
           'ASHRAE 169-2006-8A',
           'ASHRAE 169-2013-6A',
           'ASHRAE 169-2013-6B',
           'ASHRAE 169-2013-7A',
           'ASHRAE 169-2013-8A'
        # Sensor 1 controls toplighted area
        sensor_1_frac = areas['toplighted_area'] / space_area_m2
        sensor_1_window = sorted_skylights[0]
      else
        # Sensor 1 controls toplighted area
        num_sensors = 2
        sensor_1_frac = areas['toplighted_area'] / space_area_m2 / num_sensors
        sensor_1_window = sorted_skylights[0]
        sensor_2_frac = sensor_1_frac
        sensor_2_window = sensor_1_window
    end
  elsif !req_top_ctrl && req_pri_ctrl && !req_sec_ctrl
    # Sensor 1 controls primary area
    sensor_1_frac = areas['primary_sidelighted_area'] / space_area_m2
    sensor_1_window = sorted_windows[0]
  elsif !req_top_ctrl && !req_pri_ctrl && req_sec_ctrl
    # Sensor 1 controls secondary area
    sensor_1_frac = areas['secondary_sidelighted_area'] / space_area_m2
    sensor_1_window = sorted_windows[0]
  end

  return [sensor_1_frac, sensor_2_frac, sensor_1_window, sensor_2_window]
end

#space_daylighting_minimum_input_power_fraction(space) ⇒ Object

Provide the minimum input power fraction for continuous dimming daylighting control

return [Float] daylighting minimum input power fraction

Parameters:

  • space (OpenStudio::Model::Space)

    OpenStudio Space object



203
204
205
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 203

def space_daylighting_minimum_input_power_fraction(space)
  return 0.2
end

#space_infiltration_rate_75_pa(space = nil) ⇒ Double

Determine the base infiltration rate at 75 Pa.

defaults to no infiltration.

Returns:

  • (Double)

    the baseline infiltration rate, in cfm/ft^2



185
186
187
188
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 185

def space_infiltration_rate_75_pa(space = nil)
  basic_infil_rate_cfm_per_ft2 = 1.0
  return basic_infil_rate_cfm_per_ft2
end

#space_occupancy_standby_mode(thermostat) ⇒ Boolean

Modify thermostat schedule to account for a thermostat setback/up

Parameters:

  • thermostat (OpenStudio::Model::ThermostatSetpointDualSetpoint)

    OpenStudio ThermostatSetpointDualSetpoint object

Returns:

  • (Boolean)

    returns true if successful, false if not



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 40

def space_occupancy_standby_mode(thermostat)
  htg_sch = thermostat.getHeatingSchedule.get
  clg_sch = thermostat.getCoolingSchedule.get

  # Setback heating schedule
  # Setback is 1 deg. F per code requirement
  # Time of the day is arbitrary lack of dynamic occupant modeling
  setup = 1 # deg. F
  htg_sch_mod = { '12' => -1 * OpenStudio.convert(setup, 'R', 'K').get }
  htg_sch_name = "#{htg_sch.name} - occupant standby mode"
  htg_sch_old = thermostat.model.getScheduleRulesetByName(htg_sch_name)
  if htg_sch_old.empty?
    htg_sch_offset = model_offset_schedule_value(htg_sch, htg_sch_mod)
    htg_sch_offset.setName(htg_sch_name)
    thermostat.setHeatingSchedule(htg_sch_offset)
  else
    thermostat.setHeatingSchedule(htg_sch_old.get)
  end

  # Setup cooling schedule
  # Setup is 1 deg. F per code requirement
  # Time of the day is arbitrary lack of dynamic occupant modeling
  setback = 1 # deg. F
  clg_sch_mod = { '12' => OpenStudio.convert(setback, 'R', 'K').get }
  clg_sch_name = "#{clg_sch.name} - occupant standby mode"
  clg_sch_old = thermostat.model.getScheduleRulesetByName(clg_sch_name)
  if clg_sch_old.empty?
    clg_sch_offset = model_offset_schedule_value(clg_sch, clg_sch_mod)
    clg_sch_offset.setName(clg_sch_name)
    thermostat.setCoolingSchedule(clg_sch_offset)
  else
    thermostat.setCoolingSchedule(clg_sch_old.get)
  end

  return true
end

#space_occupancy_standby_mode_required?(space) ⇒ Boolean

Determine if a space should be modeled with an occupancy standby mode

Parameters:

  • space (OpenStudio::Model::Space)

    OpenStudio Space object

Returns:

  • (Boolean)

    true if occupancy standby mode is to be modeled, false otherwise



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb', line 8

def space_occupancy_standby_mode_required?(space)
  # Get space type
  return false if space.spaceType.empty?

  space_type = space.spaceType.get

  # Get standards space type
  return false if space_type.standardsSpaceType.empty?

  std_space_type = space_type.standardsSpaceType.get

  # Space with standby mode are determined based
  # on note H in Std 62.1 and their automatic partial
  # of full off lighting control requirement in 90.1.
  # In 90.1-2019/62.1-2016 this comes down to office
  # spaces (enclosed =< 250 ft2) and conference/meeting
  # and multipurpose rooms.
  # Currently standards doesn't excatly use the 90.1
  # space description so all spaces types that include
  # office/meeting/conference are flagged as having
  # occupant standby mode.
  if std_space_type.downcase.include?('office') || std_space_type.downcase.include?('meeting') || std_space_type.downcase.include?('conference')
    return true
  end

  return false
end

#thermal_zone_demand_control_ventilation_limits(thermal_zone) ⇒ Array<Double>

Determine the area and occupancy level limits for demand control ventilation.

and the minimum occupancy density in m^2/person. Returns nil if there is no requirement.

Parameters:

  • thermal_zone (OpenStudio::Model::ThermalZone)

    the thermal zone

Returns:

  • (Array<Double>)

    the minimum area, in m^2



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ThermalZone.rb', line 46

def thermal_zone_demand_control_ventilation_limits(thermal_zone)
  min_area_ft2 = 500
  min_occ_per_1000_ft2 = 25

  # Convert to SI
  min_area_m2 = OpenStudio.convert(min_area_ft2, 'ft^2', 'm^2').get
  min_occ_per_ft2 = min_occ_per_1000_ft2 / 1000.0
  min_ft2_per_occ = 1.0 / min_occ_per_ft2
  min_m2_per_occ = OpenStudio.convert(min_ft2_per_occ, 'ft^2', 'm^2').get

  return [min_area_m2, min_m2_per_occ]
end

#thermal_zone_occupancy_type(thermal_zone) ⇒ String

TODO:

Add public assembly building types

Determine the thermal zone’s occupancy type category. Options are: residential, nonresidential, publicassembly, retail

Returns:

  • (String)

    the occupancy type category



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ThermalZone.rb', line 9

def thermal_zone_occupancy_type(thermal_zone)
  occ_type = if OpenstudioStandards::ThermalZone.thermal_zone_residential?(thermal_zone)
               'residential'
             else
               'nonresidential'
             end

  # Based on the space type that
  # represents a majority of the zone.
  space_type = OpenstudioStandards::ThermalZone.thermal_zone_get_space_type(thermal_zone)
  if space_type.is_initialized
    space_type = space_type.get
    bldg_type = space_type.standardsBuildingType
    if bldg_type.is_initialized
      bldg_type = bldg_type.get
      case bldg_type
      when 'Retail', 'StripMall', 'SuperMarket'
        occ_type = 'retail'
        # when 'SomeBuildingType'
        # @todo add publicassembly building types
        # occ_type = 'publicassembly'
      end
    end
  end

  # OpenStudio::logFree(OpenStudio::Info, "openstudio.Standards.ThermalZone", "For #{self.name}, occupancy type = #{occ_type}.")

  return occ_type
end

#water_heater_mixed_additional_search_criteria(water_heater_mixed, search_criteria) ⇒ Hash

Add additional search criteria for water heater lookup efficiency.

Parameters:

  • water_heater_mixed (OpenStudio::Model::WaterHeaterMixed)

    water heater mixed object

  • search_criteria (Hash)

    search criteria for looking up water heater data

Returns:

  • (Hash)

    updated search criteria



7
8
9
10
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.WaterHeaterMixed.rb', line 7

def water_heater_mixed_additional_search_criteria(water_heater_mixed, search_criteria)
  search_criteria['draw_profile'] = 'medium' # assumption; could be based on inputs
  return search_criteria
end

#zone_hvac_component_vestibule_heating_control_required?(zone_hvac_component) ⇒ Boolean

Determine if vestibule heating control is required. Required for 90.1-2019 per 6.4.3.9.

Parameters:

  • zone_hvac_component (OpenStudio::Model::ZoneHVACComponent)

    zone hvac component

Returns:

  • (Boolean)

    returns true if successful, false if not



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ZoneHVACComponent.rb', line 10

def zone_hvac_component_vestibule_heating_control_required?(zone_hvac_component)
  # Ensure that the equipment is assigned to a thermal zone
  if zone_hvac_component.thermalZone.empty?
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.ZoneHVACComponent', "For #{zone_hvac_component.name}: equipment is not assigned to a thermal zone, cannot apply vestibule heating control.")
    return false
  end

  # Only applies to equipment that is in vestibule zones
  return true if OpenstudioStandards::ThermalZone.thermal_zone_vestibule?(zone_hvac_component.thermalZone.get)

  # If here, vestibule heating control not required
  return false
end

#zone_hvac_model_standby_mode_occupancy_control(zone_hvac_component) ⇒ Boolean

Add occupant standby controls to zone equipment Currently, the controls consists of cycling the fan during the occupant standby mode hours

Parameters:

  • zone_hvac_component

    OpenStudio zonal equipment object

Returns:

  • (Boolean)

    true if sucessful, false otherwise



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ZoneHVACComponent.rb', line 30

def zone_hvac_model_standby_mode_occupancy_control(zone_hvac_component)
  # Ensure that the equipment is assigned to a thermal zone
  if zone_hvac_component.thermalZone.empty?
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.ZoneHVACComponent', "For #{zone_hvac_component.name}: equipment is not assigned to a thermal zone, cannot apply vestibule heating control.")
    return true
  end

  # Get supply fan
  # Only Fan:OnOff can cycle
  fan = zone_hvac_component.supplyAirFan
  return true unless fan.to_FanOnOff.is_initialized

  fan = fan.to_FanOnOff.get
  # Set fan operating schedule during assumed occupant standby mode time to 0 so the fan can cycle
  # ZoneHVACFourPipeFanCoil has it optional, PTAC/PTHP starting a 3.5.0 is required
  new_sch = model_set_schedule_value(OpenStudio::Model::OptionalSchedule.new(zone_hvac_component.supplyAirFanOperatingModeSchedule).get, '12' => 0)
  zone_hvac_component.setSupplyAirFanOperatingModeSchedule(new_sch) unless new_sch == true

  return true
end