lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb in openstudio-standards-0.1.3 vs lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb in openstudio-standards-0.1.4
- old
+ new
@@ -14,65 +14,65 @@
# TODO enable damper position adjustment for legacy IDFS
if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004'
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "Damper positions not modified for DOE Ref Pre-1980 or DOE Ref 1980-2004 vintages.")
return true
end
-
+
# First time adjustment:
# Only applies to multi-zone vav systems
- # exclusion: for Outpatient: (1) both AHU1 and AHU2 in 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004'
+ # exclusion: for Outpatient: (1) both AHU1 and AHU2 in 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004'
# (2) AHU1 in 2004-2013
if self.is_multizone_vav_system && !(self.name.to_s.include? "Outpatient F1")
self.adjust_minimum_vav_damper_positions
end
-
+
# Second time adjustment:
# Only apply to 2010 and 2013 Outpatient (both AHU1 and AHU2)
# TODO maybe apply to hospital as well?
if (self.name.to_s.include? "Outpatient") && (template == '90.1-2010' || template == '90.1-2013')
self.adjust_minimum_vav_damper_positions_outpatient
end
-
+
return true
-
- end
+ end
+
# Apply all standard required controls to the airloop
#
# @param (see #is_economizer_required)
# @return [Bool] returns true if successful, false if not
# @todo optimum start
# @todo night damper shutoff
# @todo nightcycle control
# @todo night fan shutoff
def apply_standard_controls(template, climate_zone)
-
+
# Energy Recovery Ventilation
if self.is_energy_recovery_ventilator_required(template, climate_zone)
self.apply_energy_recovery_ventilator
end
-
+
# Economizers
self.set_economizer_limits(template, climate_zone)
- self.set_economizer_integration(template, climate_zone)
-
+ self.set_economizer_integration(template, climate_zone)
+
# Multizone VAV Systems
if self.is_multizone_vav_system
-
+
# VAV Reheat Control
self.set_vav_damper_action(template)
-
+
# Multizone VAV Optimization
# This rule does not apply to two hospital and one outpatient systems (TODO add hospital two systems as exception)
if !(self.name.to_s.include? "Outpatient F1")
if self.is_multizone_vav_optimization_required(template, climate_zone)
self.enable_multizone_vav_optimization
else
self.disable_multizone_vav_optimization
end
end
-
+
# VAV Static Pressure Reset
# assume all systems have DDC control of VAV terminals
has_ddc = true
if self.is_static_pressure_reset_required(template, has_ddc)
self.supply_return_exhaust_relief_fans.each do |fan|
@@ -81,13 +81,13 @@
else
OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC","For #{self.name}: there is a constant volume fan on a multizone vav system. Cannot apply static pressure reset controls.")
end
end
end
-
+
end
-
+
# Single zone systems
# if self.thermalZones.size == 1
# self.apply_single_zone_controls(template, climate_zone)
# end
@@ -106,72 +106,72 @@
# TODO Prototype buildings use OAT-based SAT reset,
# but PRM RM suggests Warmest zone based SAT reset.
if self.is_supply_air_temperature_reset_required(template, climate_zone)
self.enable_supply_air_temperature_reset_outdoor_temperature
# self.enable_supply_air_temperature_reset_warmest_zone(template)
- end
-
+ end
+
# Unoccupied shutdown
if self.is_unoccupied_fan_shutoff_required(template)
self.enable_unoccupied_fan_shutoff
else
self.setAvailabilitySchedule(self.model.alwaysOnDiscreteSchedule)
end
-
+
# Motorized OA damper
if self.is_motorized_oa_damper_required(template, climate_zone)
# Assume that the availability schedule has already been
# set to reflect occupancy and use this for the OA damper.
self.add_motorized_oa_damper(0.15, self.availabilitySchedule)
else
self.remove_motorized_oa_damper
end
-
+
# TODO Optimum Start
# for systems exceeding 10,000 cfm
# Don't think that OS will be able to do this.
# OS currently only allows 1 availability manager
- # at a time on an AirLoopHVAC. If we add an
- # AvailabilityManager:OptimumStart, it
+ # at a time on an AirLoopHVAC. If we add an
+ # AvailabilityManager:OptimumStart, it
# will replace the AvailabilityManager:NightCycle.
-
- end
+ end
+
# Apply all PRM baseline required controls to the airloop.
# Only applies those controls that differ from the normal
# prescriptive controls, which are added via
# AirLoopHVAC.apply_standard_controls
#
# @param (see #is_economizer_required)
# @return [Bool] returns true if successful, false if not
def apply_performance_rating_method_baseline_controls(template, climate_zone)
-
+
# Economizers
if self.is_performance_rating_method_baseline_economizer_required(template, climate_zone)
self.apply_performance_rating_method_baseline_economizer(template, climate_zone)
end
# Multizone VAV Systems
if self.is_multizone_vav_system
- # SAT Reset
+ # SAT Reset
# G3.1.3.12 SAT reset required for all Multizone VAV systems,
# even if not required by prescriptive section.
case template
when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
self.enable_supply_air_temperature_reset_warmest_zone(template)
end
end
-
+
# Unoccupied shutdown
self.enable_unoccupied_fan_shutoff
-
+
return true
-
- end
+ end
+
# Calculate and apply the performance rating method
# baseline fan power to this air loop.
# Fan motor efficiency will be set, and then
# fan pressure rise adjusted so that the
# fan power is the maximum allowable.
@@ -180,22 +180,22 @@
#
# @todo Figure out how to split fan power between multiple fans
# if the proposed model had multiple fans (supply, return, exhaust, etc.)
# return [Bool] true if successful, false if not.
def set_performance_rating_method_baseline_fan_power(template)
-
+
# Main AHU fans
-
+
# Calculate the allowable fan motor bhp
# for the entire airloop.
allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
# Divide the allowable power evenly between the fans
# on this airloop.
- all_fans = self.supply_return_exhaust_relief_fans
- allowable_fan_bhp = allowable_fan_bhp / all_fans.size
-
+ all_fans = self.supply_return_exhaust_relief_fans
+ allowable_fan_bhp = allowable_fan_bhp / all_fans.size
+
# Set the motor efficiencies
# for all fans based on the calculated
# allowed brake hp. Then calculate the allowable
# fan power for each fan and adjust
# the fan pressure rise accordingly
@@ -204,31 +204,31 @@
allowable_power_w = allowable_fan_bhp * 746 / fan.motorEfficiency
fan.adjust_pressure_rise_to_meet_fan_power(allowable_power_w)
end
# Fan powered terminal fans
-
+
# Adjust each terminal fan
self.demandComponents.each do |dc|
next if dc.to_AirTerminalSingleDuctParallelPIUReheat.empty?
pfp_term = dc.to_AirTerminalSingleDuctParallelPIUReheat.get
pfp_term.set_performance_rating_method_baseline_fan_power(template)
end
-
+
return true
-
+
end
-
+
# Determine the fan power limitation pressure drop adjustment
# Per Table 6.5.3.1.1B
#
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
# @return [Double] fan power limitation pressure drop adjustment
# units = horsepower
# @todo Determine the presence of MERV filters and other stuff in Table 6.5.3.1.1B. May need to extend AirLoopHVAC data model
def fan_power_limitation_pressure_drop_adjustment_brake_horsepower(template = "ASHRAE 90.1-2007")
-
+
# Get design supply air flow rate (whether autosized or hard-sized)
dsn_air_flow_m3_per_s = 0
dsn_air_flow_cfm = 0
if self.autosizedDesignSupplyAirFlowRate.is_initialized
dsn_air_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
@@ -236,44 +236,44 @@
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
else
dsn_air_flow_m3_per_s = self.designSupplyAirFlowRate.get
dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
- end
-
+ end
+
# TODO determine the presence of MERV filters and other stuff
# in Table 6.5.3.1.1B
# perhaps need to extend AirLoopHVAC data model
has_fully_ducted_return_and_or_exhaust_air_systems = false
-
+
# Calculate Fan Power Limitation Pressure Drop Adjustment (in wc)
fan_pwr_adjustment_in_wc = 0
-
+
# Fully ducted return and/or exhaust air systems
if has_fully_ducted_return_and_or_exhaust_air_systems
adj_in_wc = 0.5
fan_pwr_adjustment_in_wc += adj_in_wc
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","--Added #{adj_in_wc} in wc for Fully ducted return and/or exhaust air systems")
end
-
+
# Convert the pressure drop adjustment to brake horsepower (bhp)
# assuming that all supply air passes through all devices
fan_pwr_adjustment_bhp = fan_pwr_adjustment_in_wc*dsn_air_flow_cfm / 4131
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: Fan Power Limitation Pressure Drop Adjustment = #{(fan_pwr_adjustment_bhp.round(2))} bhp")
-
+
return fan_pwr_adjustment_bhp
-
+
end
# Determine the allowable fan system brake horsepower
# Per Table 6.5.3.1.1A
#
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
# @return [Double] allowable fan system brake horsepower
# units = horsepower
def allowable_system_brake_horsepower(template = "ASHRAE 90.1-2007")
-
+
# Get design supply air flow rate (whether autosized or hard-sized)
dsn_air_flow_m3_per_s = 0
dsn_air_flow_cfm = 0
if self.autosizedDesignSupplyAirFlowRate.is_initialized
dsn_air_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
@@ -285,14 +285,14 @@
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
end
# Get the fan limitation pressure drop adjustment bhp
fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
-
+
# Determine the number of zones the system serves
num_zones_served = self.thermalZones.size
-
+
# Get the supply air fan and determine whether VAV or CAV system.
# Assume that supply air fan is fan closest to the demand outlet node.
# The fan may be inside of a piece of unitary equipment.
fan_pwr_limit_type = nil
self.supplyComponents.reverse.each do |comp|
@@ -312,53 +312,53 @@
if fan.to_FanConstantVolume.is_initialized || comp.to_FanOnOff.is_initialized
fan_pwr_limit_type = "constant volume"
elsif fan.to_FanVariableVolume.is_initialized
fan_pwr_limit_type = "variable volume"
end
- end
+ end
end
-
- # For 90.1-2010, single-zone VAV systems use the
+
+ # For 90.1-2010, single-zone VAV systems use the
# constant volume limitation per 6.5.3.1.1
if template == "ASHRAE 90.1-2010" && fan_pwr_limit_type = "variable volume" && num_zones_served == 1
fan_pwr_limit_type = "constant volume"
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Using the constant volume limitation because single-zone VAV system.")
end
-
+
# Calculate the Allowable Fan System brake horsepower per Table G3.1.2.9
allowable_fan_bhp = 0
if fan_pwr_limit_type == "constant volume"
allowable_fan_bhp = dsn_air_flow_cfm*0.00094+fan_pwr_adjustment_bhp
elsif fan_pwr_limit_type == "variable volume"
allowable_fan_bhp = dsn_air_flow_cfm*0.0013+fan_pwr_adjustment_bhp
end
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Allowable brake horsepower = #{(allowable_fan_bhp).round(2)}HP based on #{dsn_air_flow_cfm.round} cfm and #{fan_pwr_adjustment_bhp.round(2)} bhp of adjustment.")
-
+
# Calculate and report the total area for debugging/testing
floor_area_served_m2 = self.floor_area_served
floor_area_served_ft2 = OpenStudio.convert(floor_area_served_m2, 'm^2', 'ft^2').get
cfm_per_ft2 = dsn_air_flow_cfm / floor_area_served_ft2
cfm_per_hp = dsn_air_flow_cfm / allowable_fan_bhp
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: area served = #{floor_area_served_ft2.round} ft^2.")
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per area = #{cfm_per_ft2.round} cfm/ft^2.")
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per hp = #{cfm_per_hp.round} cfm/hp.")
-
+
return allowable_fan_bhp
end
# Get all of the supply, return, exhaust, and relief fans on this system
#
# @return [Array] an array of FanConstantVolume, FanVariableVolume, and FanOnOff objects
- def supply_return_exhaust_relief_fans()
-
+ def supply_return_exhaust_relief_fans()
+
# Fans on the supply side of the airloop directly, or inside of unitary equipment.
fans = []
sup_and_oa_comps = self.supplyComponents
sup_and_oa_comps += self.oaComponents
sup_and_oa_comps.each do |comp|
- if comp.to_FanConstantVolume.is_initialized
+ if comp.to_FanConstantVolume.is_initialized
fans << comp.to_FanConstantVolume.get
elsif comp.to_FanVariableVolume.is_initialized
fans << comp.to_FanVariableVolume.get
elsif comp.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.is_initialized
sup_fan = comp.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.get.supplyAirFan
@@ -374,37 +374,37 @@
if sup_fan.to_FanConstantVolume.is_initialized
fans << sup_fan.to_FanConstantVolume.get
elsif sup_fan.to_FanOnOff.is_initialized
fans << sup_fan.to_FanOnOff.get
elsif sup_fan.to_FanVariableVolume.is_initialized
- fans << sup_fan.to_FanVariableVolume.get
- end
+ fans << sup_fan.to_FanVariableVolume.get
+ end
end
- end
-
+ end
+
return fans
-
+
end
-
+
# Determine the total brake horsepower of the fans on the system
# with or without the fans inside of fan powered terminals.
#
# @param include_terminal_fans [Bool] if true, power from fan powered terminals will be included
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
# @return [Double] total brake horsepower of the fans on the system
- # units = horsepower
+ # units = horsepower
def system_fan_brake_horsepower(include_terminal_fans = true, template = "ASHRAE 90.1-2007")
# TODO get the template from the parent model itself?
# Or not because maybe you want to see the difference between two standards?
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","#{self.name}-Determining #{template} allowable system fan power.")
-
+
# Get all fans
fans = []
# Supply, exhaust, relief, and return fans
fans += self.supply_return_exhaust_relief_fans
-
+
# Fans inside of fan-powered terminals
if include_terminal_fans
self.demandComponents.each do |comp|
if comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
term_fan = comp.to_AirTerminalSingleDuctSeriesPIUReheat.get.supplyAirFan
@@ -413,80 +413,80 @@
end
elsif comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized
term_fan = comp.to_AirTerminalSingleDuctParallelPIUReheat.get.fan
if term_fan.to_FanConstantVolume.is_initialized
fans << term_fan.to_FanConstantVolume.get
- end
+ end
end
end
end
-
+
# Loop through all fans on the system and
# sum up their brake horsepower values.
sys_fan_bhp = 0
fans.sort.each do |fan|
sys_fan_bhp += fan.brakeHorsepower
end
-
+
return sys_fan_bhp
-
- end
-
+
+ end
+
# Set the fan pressure rises that will result in
# the system hitting the baseline allowable fan power
#
- # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
+ # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
def set_baseline_fan_pressure_rise(template = "ASHRAE 90.1-2007")
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "#{self.name}-Setting #{template} baseline fan power.")
-
+
# Get the total system bhp from the proposed system, including terminal fans
proposed_sys_bhp = self.system_fan_brake_horsepower(true)
-
+
# Get the allowable fan brake horsepower
allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
# Get the fan power limitation from proposed system
fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
-
+
# Subtract the fan power adjustment
allowable_fan_bhp = allowable_fan_bhp-fan_pwr_adjustment_bhp
-
+
# Get all fans
- fans = self.supply_return_exhaust_relief_fans
-
+ fans = self.supply_return_exhaust_relief_fans
+
# TODO improve description
# Loop through the fans, changing the pressure rise
# until the fan bhp is the same percentage of the baseline allowable bhp
# as it was on the proposed system.
fans.each do |fan|
# TODO: Yixing Check the model of the Fan Coil Unit
next if fan.name.to_s.include?("Fan Coil fan")
next if fan.name.to_s.include?("UnitHeater Fan")
OpenStudio::logFree(OpenStudio::Info, "#{fan.name}")
-
+
# Get the bhp of the fan on the proposed system
proposed_fan_bhp = fan.brakeHorsepower
-
+
# Get the bhp of the fan on the proposed system
proposed_fan_bhp_frac = proposed_fan_bhp / proposed_sys_bhp
-
+
# Determine the target bhp of the fan on the baseline system
baseline_fan_bhp = proposed_fan_bhp_frac*allowable_fan_bhp
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_fan_bhp).round(1)} bhp = Baseline fan brake horsepower.")
-
- # Set the baseline impeller eff of the fan,
+
+ # Set the baseline impeller eff of the fan,
# preserving the proposed motor eff.
baseline_impeller_eff = fan.baselineImpellerEfficiency(template)
fan.changeImpellerEfficiency(baseline_impeller_eff)
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_impeller_eff*100).round(1)}% = Baseline fan impeller efficiency.")
-
+
# Set the baseline motor efficiency for the specified bhp
baseline_motor_eff = fan.standardMinimumMotorEfficiency(template, standards, allowable_fan_bhp)
fan.changeMotorEfficiency(baseline_motor_eff)
-
+
# Get design supply air flow rate (whether autosized or hard-sized)
dsn_air_flow_m3_per_s = 0
if fan.autosizedDesignSupplyAirFlowRate.is_initialized
dsn_air_flow_m3_per_s = fan.autosizedDesignSupplyAirFlowRate.get
dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
@@ -494,11 +494,11 @@
else
dsn_air_flow_m3_per_s = fan.designSupplyAirFlowRate.get
dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = User entered Design Supply Air Flow Rate.")
end
-
+
# Determine the fan pressure rise that will result in the target bhp
# pressure_rise_pa = fan_bhp*746 / fan_motor_eff*fan_total_eff / dsn_air_flow_m3_per_s
baseline_pressure_rise_pa = baseline_fan_bhp*746 / fan.motorEfficiency*fan.fanEfficiency / dsn_air_flow_m3_per_s
baseline_pressure_rise_in_wc = OpenStudio.convert(fan_pressure_rise_pa, "Pa", "inH_{2}O",).get
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(fan_pressure_rise_in_wc).round(2)} in w.c. = Pressure drop to achieve allowable fan power.")
@@ -508,11 +508,11 @@
if ((calc_bhp-baseline_fan_bhp) / baseline_fan_bhp).abs > 0.02
OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC", "#{fan.name} baseline fan bhp supposed to be #{baseline_fan_bhp}, but is #{calc_bhp}.")
end
end
-
+
# Calculate the total bhp of the system to make sure it matches the goal
calc_sys_bhp = self.system_fan_brake_horsepower(false)
if ((calc_sys_bhp-allowable_fan_bhp) / allowable_fan_bhp).abs > 0.02
OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC", "#{self.name} baseline system bhp supposed to be #{allowable_fan_bhp}, but is #{calc_sys_bhp}.")
end
@@ -524,11 +524,11 @@
# @return [Double] total cooling capacity
# units = Watts (W)
# @todo Change to pull water coil nominal capacity instead of design load; not a huge difference, but water coil nominal capacity not available in sizing table.
# @todo Handle all additional cooling coil types. Currently only handles CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, and CoilCoolingWater
def total_cooling_capacity
-
+
# Sum the cooling capacity for all cooling components
# on the airloop, which may be inside of unitary systems.
total_cooling_capacity_w = 0
self.supplyComponents.each do |sc|
# CoilCoolingDXSingleSpeed
@@ -540,11 +540,11 @@
total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
end
# CoilCoolingDXTwoSpeed
- elsif sc.to_CoilCoolingDXTwoSpeed.is_initialized
+ elsif sc.to_CoilCoolingDXTwoSpeed.is_initialized
coil = sc.to_CoilCoolingDXTwoSpeed.get
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
@@ -558,11 +558,11 @@
total_cooling_capacity_w += coil.autosizedDesignCoilLoad.get
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
end
# CoilCoolingWaterToAirHeatPumpEquationFit
- elsif sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
+ elsif sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
coil = sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
if coil.ratedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.ratedTotalCoolingCapacity.get
elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
@@ -582,11 +582,11 @@
total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
end
# CoilCoolingDXTwoSpeed
- elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
+ elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
coil = clg_coil.to_CoilCoolingDXTwoSpeed.get
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
@@ -600,11 +600,11 @@
total_cooling_capacity_w += coil.autosizedDesignCoilLoad.get
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
end
# CoilCoolingWaterToAirHeatPumpEquationFit
- elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
+ elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
coil = clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
if coil.ratedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.ratedTotalCoolingCapacity.get
elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
@@ -625,11 +625,11 @@
total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
end
# CoilCoolingDXTwoSpeed
- elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
+ elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
coil = clg_coil.to_CoilCoolingDXTwoSpeed.get
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
@@ -653,46 +653,46 @@
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "#{self.name} has a cooling coil named #{sc.name}, whose type is not yet covered by economizer checks.")
# CoilCoolingDXMultiSpeed
# CoilCoolingCooledBeam
# CoilCoolingWaterToAirHeatPumpEquationFit
# AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass
- # AirLoopHVACUnitaryHeatPumpAirToAir
- # AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed
+ # AirLoopHVACUnitaryHeatPumpAirToAir
+ # AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed
# AirLoopHVACUnitarySystem
end
end
return total_cooling_capacity_w
-
+
end
-
+
# Determine whether or not this system
# is required to have an economizer.
#
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
# @param climate_zone [String] valid choices: '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-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-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
# @return [Bool] returns true if an economizer is required, false if not
def is_economizer_required(template, climate_zone)
-
+
economizer_required = false
-
+
return economizer_required if self.name.to_s.include? "Outpatient F1"
-
+
# A big number of btu per hr as the minimum requirement
infinity_btu_per_hr = 999999999999
minimum_capacity_btu_per_hr = infinity_btu_per_hr
-
+
# Determine if the airloop serves any computer rooms
# / data centers, which changes the economizer.
is_dc = false
if self.data_center_area_served > 0
is_dc = true
end
-
+
# Determine the minimum capacity that requires an economizer
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
case climate_zone
when 'ASHRAE 169-2006-1A',
@@ -732,21 +732,21 @@
'ASHRAE 169-2006-6A',
'ASHRAE 169-2006-7A',
'ASHRAE 169-2006-7B',
'ASHRAE 169-2006-8A',
'ASHRAE 169-2006-8B'
- minimum_capacity_btu_per_hr = 135000
+ minimum_capacity_btu_per_hr = 135000
when 'ASHRAE 169-2006-3B',
'ASHRAE 169-2006-3C',
'ASHRAE 169-2006-4B',
'ASHRAE 169-2006-4C',
'ASHRAE 169-2006-5B',
'ASHRAE 169-2006-5C',
'ASHRAE 169-2006-6B'
minimum_capacity_btu_per_hr = 65000
end
- else
+ else
case climate_zone
when 'ASHRAE 169-2006-1A',
'ASHRAE 169-2006-1B'
minimum_capacity_btu_per_hr = infinity_btu_per_hr # No requirement
when 'ASHRAE 169-2006-2A',
@@ -770,11 +770,11 @@
end
end
when 'NECB 2011'
minimum_capacity_btu_per_hr = 68243 # NECB requires economizer for cooling cap > 20 kW
end
-
+
# Check whether the system requires an economizer by comparing
# the system capacity to the minimum capacity.
total_cooling_capacity_w = self.total_cooling_capacity
total_cooling_capacity_btu_per_hr = OpenStudio.convert(total_cooling_capacity_w, "W", "Btu/hr").get
if total_cooling_capacity_btu_per_hr >= minimum_capacity_btu_per_hr
@@ -787,49 +787,49 @@
else
if is_dc
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
else
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
- end
+ end
end
-
+
return economizer_required
-
+
end
-
+
# Set the economizer limits per the standard. Limits are based on the economizer
# type currently specified in the ControllerOutdoorAir object on this air loop.
#
# @param (see #is_economizer_required)
# @return [Bool] returns true if successful, false if not
def set_economizer_limits(template, climate_zone)
-
+
# EnergyPlus economizer types
# 'NoEconomizer'
# 'FixedDryBulb'
# 'FixedEnthalpy'
# 'DifferentialDryBulb'
# 'DifferentialEnthalpy'
# 'FixedDewPointAndDryBulb'
# 'ElectronicEnthalpy'
- # 'DifferentialDryBulbAndEnthalpy'
-
+ # 'DifferentialDryBulbAndEnthalpy'
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
economizer_type = oa_control.getEconomizerControlType
-
+
# Return false if no economizer is present
if economizer_type == 'NoEconomizer'
return false
end
-
+
# Determine the limits according to the type
drybulb_limit_f = nil
enthalpy_limit_btu_per_lb = nil
dewpoint_limit_f = nil
case template
@@ -892,12 +892,12 @@
enthalpy_limit_btu_per_lb = 28
when 'FixedDewPointAndDryBulb'
drybulb_limit_f = 75
dewpoint_limit_f = 55
end
- end
-
+ end
+
# Set the limits
case economizer_type
when 'FixedDryBulb'
if drybulb_limit_f
drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
@@ -916,25 +916,25 @@
dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
end
- end
+ end
return true
-
+
end
# For systems required to have an economizer, set the economizer
# to integrated on non-integrated per the standard.
#
# @note this method assumes you previously checked that an economizer is required at all
# via #is_economizer_required
# @param (see #is_economizer_required)
# @return [Bool] returns true if successful, false if not
def set_economizer_integration(template, climate_zone)
-
+
# Determine if the system is a VAV system based on the fan
# which may be inside of a unitary system.
is_vav = false
self.supplyComponents.reverse.each do |comp|
if comp.to_FanVariableVolume.is_initialized
@@ -949,24 +949,24 @@
if fan.is_initialized
if fan.get.to_FanVariableVolume.is_initialized
is_vav = true
end
end
- end
+ end
end
# Determine the number of zones the system serves
num_zones_served = self.thermalZones.size
-
+
# A big number of btu per hr as the minimum requirement
infinity_btu_per_hr = 999999999999
minimum_capacity_btu_per_hr = infinity_btu_per_hr
-
+
# Determine if an integrated economizer is required
integrated_economizer_required = true
case template
- when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
+ when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
minimum_capacity_btu_per_hr = 65000
minimum_capacity_w = OpenStudio.convert(minimum_capacity_btu_per_hr, "Btu/hr", "W").get
# 6.5.1.3 Integrated Economizer Control
# Exception a, DX VAV systems
if is_vav == true && num_zones_served > 1
@@ -1005,47 +1005,47 @@
end
when '90.1-2010', '90.1-2013'
integrated_economizer_required = true
when 'NECB 2011'
# this means that compressor allowed to turn on when economizer is open
- # (NoLockout); as per 5.2.2.8(3)
+ # (NoLockout); as per 5.2.2.8(3)
integrated_economizer_required = true
end
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
- oa_control = oa_sys.getControllerOutdoorAir
-
+ oa_control = oa_sys.getControllerOutdoorAir
+
# Apply integrated or non-integrated economizer
if integrated_economizer_required
oa_control.setLockoutType('NoLockout')
else
oa_control.setLockoutType('LockoutWithCompressor')
end
return true
-
+
end
-
+
# Determine if an economizer is required per the PRM.
#
# @param (see #is_economizer_required)
# @return [Bool] returns true if required, false if not
def is_performance_rating_method_baseline_economizer_required(template, climate_zone)
-
+
economizer_required = false
-
+
# A big number of ft2 as the minimum requirement
infinity_ft2 = 999999999999
min_int_area_served_ft2 = infinity_ft2
min_ext_area_served_ft2 = infinity_ft2
-
+
# Determine the minimum capacity that requires an economizer
case template
when '90.1-2004'
case climate_zone
when 'ASHRAE 169-2006-1A',
@@ -1081,58 +1081,58 @@
'ASHRAE 169-2006-2A',
'ASHRAE 169-2006-3A',
'ASHRAE 169-2006-4A'
min_int_area_served_ft2 = infinity_ft2 # No requirement
min_ext_area_served_ft2 = infinity_ft2 # No requirement
- else
+ else
min_int_area_served_ft2 = 0 # Always required
min_ext_area_served_ft2 = 0 # Always required
end
end
-
+
# Check whether the system requires an economizer by comparing
# the system capacity to the minimum capacity.
min_int_area_served_m2 = OpenStudio.convert(min_int_area_served_ft2, "ft^2", "m^2").get
min_ext_area_served_m2 = OpenStudio.convert(min_ext_area_served_ft2, "ft^2", "m^2").get
-
+
# Get the interior and exterior area served
int_area_served_m2 = self.floor_area_served_interior_zones
ext_area_served_m2 = self.floor_area_served_exterior_zones
-
+
# Check the floor area exception
if int_area_served_m2 < min_int_area_served_m2 && ext_area_served_m2 < min_ext_area_served_m2
if min_int_area_served_ft2 == infinity_ft2 && min_ext_area_served_ft2 == infinity_ft2
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer not required for climate zone #{climate_zone}.")
else
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer not required for because the interior area served of #{int_area_served_m2} ft2 < minimum of #{min_int_area_served_m2} and the perimeter area served of #{ext_area_served_m2} ft2 < minimum of #{min_ext_area_served_m2} for climate zone #{climate_zone}.")
end
return economizer_required
end
-
+
# If here, economizer required
economizer_required = true
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer required for the performance rating method baseline.")
-
- return economizer_required
+ return economizer_required
+
end
-
+
# Apply the PRM economizer type and set temperature limits
#
# @param (see #is_economizer_required)
# @return [Bool] returns true if successful, false if not
def apply_performance_rating_method_baseline_economizer(template, climate_zone)
-
+
# EnergyPlus economizer types
# 'NoEconomizer'
# 'FixedDryBulb'
# 'FixedEnthalpy'
# 'DifferentialDryBulb'
# 'DifferentialEnthalpy'
# 'FixedDewPointAndDryBulb'
# 'ElectronicEnthalpy'
- # 'DifferentialDryBulbAndEnthalpy'
+ # 'DifferentialDryBulbAndEnthalpy'
# Determine the type and limits
economizer_type = nil
drybulb_limit_f = nil
enthalpy_limit_btu_per_lb = nil
@@ -1176,37 +1176,37 @@
'ASHRAE 169-2006-6B',
'ASHRAE 169-2006-7A',
'ASHRAE 169-2006-7B',
'ASHRAE 169-2006-8A',
'ASHRAE 169-2006-8B'
- economizer_type = 'FixedDryBulb'
+ economizer_type = 'FixedDryBulb'
drybulb_limit_f = 75
when 'ASHRAE 169-2006-2A',
'ASHRAE 169-2006-3A',
'ASHRAE 169-2006-4A'
economizer_type = 'FixedEnthalpy'
enthalpy_limit_btu_per_lb = 28
when 'ASHRAE 169-2006-5A',
'ASHRAE 169-2006-6A',
'ASHRAE 169-2006-7A'
- economizer_type = 'FixedDryBulb'
+ economizer_type = 'FixedDryBulb'
drybulb_limit_f = 70
else
- economizer_type = 'FixedDryBulb'
+ economizer_type = 'FixedDryBulb'
drybulb_limit_f = 65
end
end
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
-
+
# Set the limits
case economizer_type
when 'FixedDryBulb'
if drybulb_limit_f
drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
@@ -1225,49 +1225,49 @@
dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
end
- end
+ end
return true
end
-
+
# Check the economizer type currently specified in the ControllerOutdoorAir object on this air loop
# is acceptable per the standard.
#
# @param (see #is_economizer_required)
# @return [Bool] Returns true if allowable, if the system has no economizer or no OA system.
# Returns false if the economizer type is not allowable.
def is_economizer_type_allowable(template, climate_zone)
-
+
# EnergyPlus economizer types
# 'NoEconomizer'
# 'FixedDryBulb'
# 'FixedEnthalpy'
# 'DifferentialDryBulb'
# 'DifferentialEnthalpy'
# 'FixedDewPointAndDryBulb'
# 'ElectronicEnthalpy'
# 'DifferentialDryBulbAndEnthalpy'
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return true # No OA system
end
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 minimum capacity that requires an economizer
prohibited_types = []
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
case climate_zone
@@ -1288,11 +1288,11 @@
'ASHRAE 169-2006-1A',
'ASHRAE 169-2006-2A',
'ASHRAE 169-2006-3A',
'ASHRAE 169-2006-4A'
prohibited_types = ['DifferentialDryBulb']
- when
+ when
'ASHRAE 169-2006-5A',
'ASHRAE 169-2006-6A',
prohibited_types = []
end
when '90.1-2010', '90.1-2013'
@@ -1314,69 +1314,88 @@
'ASHRAE 169-2006-1A',
'ASHRAE 169-2006-2A',
'ASHRAE 169-2006-3A',
'ASHRAE 169-2006-4A'
prohibited_types = ['FixedDryBulb', 'DifferentialDryBulb']
- when
+ when
'ASHRAE 169-2006-5A',
'ASHRAE 169-2006-6A',
prohibited_types = []
end
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
-
+
# Check if ERV is required on this airloop.
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
def is_energy_recovery_ventilator_required(template, climate_zone)
-
- # ERV Not Applicable for AHUs that serve
+ # ERV Not Applicable for AHUs that serve
# parking garage, warehouse, or multifamily
# if space_types_served_names.include?('PNNL_Asset_Rating_Apartment_Space_Type') ||
# space_types_served_names.include?('PNNL_Asset_Rating_LowRiseApartment_Space_Type') ||
# space_types_served_names.include?('PNNL_Asset_Rating_ParkingGarage_Space_Type') ||
# space_types_served_names.include?('PNNL_Asset_Rating_Warehouse_Space_Type')
# OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because it because it serves parking garage, warehouse, or multifamily.")
# return false
# end
-
+
erv_required = nil
# ERV not applicable for medical AHUs (AHU1 in Outpatient), per AIA 2001 - 7.31.D2.
if self.name.to_s.include? "Outpatient F1"
erv_required = false
return erv_required
end
-
+
+ # ERV not applicable for medical AHUs, per AIA 2001 - 7.31.D2.
+ if self.name.to_s.include? "VAV_ER"
+ erv_required = false
+ return erv_required
+ elsif self.name.to_s.include? "VAV_OR"
+ erv_required = false
+ return erv_required
+ end
+ case template
+ when '90.1-2004', '90.1-2007'
+ if self.name.to_s.include? "VAV_ICU"
+ erv_required = false
+ return erv_required
+ elsif self.name.to_s.include? "VAV_PATRMS"
+ erv_required = false
+ return erv_required
+ end
+ end
+
# ERV Not Applicable for AHUs that have DCV
- # or that have no OA intake.
+ # or that have no OA intake.
controller_oa = nil
controller_mv = nil
oa_system = nil
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
controller_mv = controller_oa.controllerMechanicalVentilation
if controller_mv.demandControlledVentilation == true
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because DCV enabled.")
return false
end
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because it has no OA intake.")
return false
end
+
# Get the AHU design supply air flow rate
dsn_flow_m3_per_s = nil
if self.designSupplyAirFlowRate.is_initialized
dsn_flow_m3_per_s = self.designSupplyAirFlowRate.get
elsif self.autosizedDesignSupplyAirFlowRate.is_initialized
@@ -1384,11 +1403,11 @@
else
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name} design supply air flow rate is not available, cannot apply efficiency standard.")
return false
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
@@ -1396,14 +1415,14 @@
else
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
return false
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
-
+
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
erv_cfm = nil # Not required
when '90.1-2004', '90.1-2007'
if pct_oa < 0.7
@@ -1425,11 +1444,11 @@
erv_cfm = nil
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = nil
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 5000
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 5000
end
when 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2B', 'ASHRAE 169-2006-5C'
if pct_oa < 0.3
erv_cfm = nil
@@ -1441,11 +1460,11 @@
erv_cfm = 26000
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = 12000
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 5000
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 4000
end
when 'ASHRAE 169-2006-6B'
if pct_oa < 0.3
erv_cfm = nil
@@ -1457,13 +1476,13 @@
erv_cfm = 4500
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = 3500
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 2500
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 1500
- end
+ end
when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-6A'
if pct_oa < 0.3
erv_cfm = nil
elsif pct_oa >= 0.3 && pct_oa < 0.4
erv_cfm = 5500
@@ -1473,13 +1492,13 @@
erv_cfm = 3500
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = 2000
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 1000
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 0
- end
+ end
when 'ASHRAE 169-2006-7A', 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
if pct_oa < 0.3
erv_cfm = nil
elsif pct_oa >= 0.3 && pct_oa < 0.4
erv_cfm = 2500
@@ -1489,13 +1508,13 @@
erv_cfm = 0
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = 0
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 0
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 0
- end
+ end
end
when '90.1-2013'
# Table 6.5.6.1-2
case climate_zone
when 'ASHRAE 169-2006-3C'
@@ -1515,11 +1534,11 @@
erv_cfm = 4000
elsif pct_oa >= 0.6 && pct_oa < 0.7
erv_cfm = 3000
elsif pct_oa >= 0.7 && pct_oa < 0.8
erv_cfm = 1500
- elsif pct_oa >= 0.8
+ elsif pct_oa >= 0.8
erv_cfm = 0
end
when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-5B'
if pct_oa < 0.1
erv_cfm = nil
@@ -1541,122 +1560,122 @@
erv_cfm = 0
end
end
when 'NECB 2011'
# The NECB 2011 requirement is that systems with an exhaust heat content > 150 kW require an HRV
- # The calculation for this is done below, to modify erv_required
+ # The calculation for this is done below, to modify erv_required
# erv_cfm set to nil here as placeholder, will lead to erv_required = false
erv_cfm = nil
end
-
+
# Determine if an ERV is required
# erv_required = nil
if erv_cfm.nil?
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}.")
- erv_required = false
+ erv_required = false
elsif dsn_flow_cfm < erv_cfm
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Does not exceed minimum flow requirement of #{erv_cfm}cfm.")
- erv_required = false
+ erv_required = false
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Exceeds minimum flow requirement of #{erv_cfm}cfm.")
- erv_required = true
+ erv_required = true
end
-
+
# This code modifies erv_required for NECB 2011
# Calculation of exhaust heat content and check whether it is > 150 kW
-
- if template == 'NECB 2011'
-
+
+ if template == 'NECB 2011'
+
# get all zones in the model
zones = self.thermalZones
-
+
# initialize counters
sum_zone_oa = 0.0
sum_zoneoaTimesheatDesignT = 0.0
-
+
# zone loop
zones.each do |zone|
-
+
# get design heat temperature for each zone; this is equivalent to design exhaust temperature
zone_sizing = zone.sizingZone
heatDesignTemp = zone_sizing.zoneHeatingDesignSupplyAirTemperature
# initialize counter
zone_oa = 0.0
# outdoor defined at space level; get OA flow for all spaces within zone
spaces = zone.spaces
-
+
# space loop
spaces.each do |space|
if not space.designSpecificationOutdoorAir.empty? # if empty, don't do anything
- outdoor_air = space.designSpecificationOutdoorAir.get
-
+ outdoor_air = space.designSpecificationOutdoorAir.get
+
# in bTAP, outdoor air specified as outdoor air per person (m3/s/person)
oa_flow_per_person = outdoor_air.outdoorAirFlowperPerson
num_people = space.peoplePerFloorArea * space.floorArea
oa_flow = oa_flow_per_person * num_people # oa flow for the space
zone_oa = zone_oa + oa_flow # add up oa flow for all spaces to get zone air flow
- end
-
+ end
+
end # space loop
-
+
sum_zone_oa = sum_zone_oa + zone_oa # sum of all zone oa flows to get system oa flow
sum_zoneoaTimesheatDesignT = sum_zoneoaTimesheatDesignT + (zone_oa * heatDesignTemp) # calculated to get oa flow weighted average of design exhaust temperature
-
+
end # zone loop
-
+
# Calculate average exhaust temperature (oa flow weighted average)
- avg_exhaust_temp = sum_zoneoaTimesheatDesignT / sum_zone_oa
-
- # for debugging/testing
+ avg_exhaust_temp = sum_zoneoaTimesheatDesignT / sum_zone_oa
+
+ # for debugging/testing
# puts "average exhaust temp = #{avg_exhaust_temp}"
# puts "sum_zone_oa = #{sum_zone_oa}"
-
+
# Get January winter design temperature
# get model weather file name
weather_file = BTAP::Environment::WeatherFile.new(self.model.weatherFile.get.path.get)
-
+
# get winter(heating) design temp stored in array
# Note that the NECB 2011 specifies using the 2.5% january design temperature
# The outdoor temperature used here is the 0.4% heating design temperature of the coldest month, available in stat file
outdoor_temp = weather_file.heating_design_info[1]
-
+
# for debugging/testing
-# puts "outdoor design temp = #{outdoor_temp}"
-
+# puts "outdoor design temp = #{outdoor_temp}"
+
# Calculate exhaust heat content
exhaust_heat_content = 0.00123 * sum_zone_oa * 1000.0 * (avg_exhaust_temp - outdoor_temp)
-
+
# for debugging/testing
# puts "exhaust heat content = #{exhaust_heat_content}"
-
-
+
+
# Modify erv_required based on exhaust heat content
if ( exhaust_heat_content > 150.0 ) then
erv_required = true
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on exhaust heat content.")
+ OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on exhaust heat content.")
else
erv_required = false
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on exhaust heat content.")
+ OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on exhaust heat content.")
end
-
-
-
+
+
+
end # of NECB 2011 condition
-
+
# for debugging/testing
-# puts "erv_required = #{erv_required}"
-
+# puts "erv_required = #{erv_required}"
+
return erv_required
-
- end
-
+
+ end
+
# Add an ERV to this airloop.
# Will be a rotary-type HX
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
def apply_energy_recovery_ventilator()
# Get the oa system
oa_system = nil
@@ -1664,11 +1683,11 @@
oa_system = self.airLoopHVACOutdoorAirSystem.get
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV cannot be added because the system has no OA intake.")
return false
end
-
+
# Create an ERV
erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(self.model)
erv.setName("#{self.name} ERV")
erv.setSensibleEffectivenessat100HeatingAirFlow(0.7)
erv.setLatentEffectivenessat100HeatingAirFlow(0.6)
@@ -1676,30 +1695,30 @@
erv.setLatentEffectivenessat75HeatingAirFlow(0.6)
erv.setSensibleEffectivenessat100CoolingAirFlow(0.75)
erv.setLatentEffectivenessat100CoolingAirFlow(0.6)
erv.setSensibleEffectivenessat75CoolingAirFlow(0.75)
erv.setLatentEffectivenessat75CoolingAirFlow(0.6)
- erv.setSupplyAirOutletTemperatureControl(true)
+ erv.setSupplyAirOutletTemperatureControl(true)
erv.setHeatExchangerType('Rotary')
erv.setFrostControlType('ExhaustOnly')
erv.setEconomizerLockout(true)
erv.setThresholdTemperature(-23.3) # -10F
erv.setInitialDefrostTimeFraction(0.167)
erv.setRateofDefrostTimeFractionIncrease(1.44)
-
+
# Add the ERV to the OA system
- erv.addToNode(oa_system.outboardOANode.get)
+ erv.addToNode(oa_system.outboardOANode.get)
# Add a setpoint manager OA pretreat
# to control the ERV
spm_oa_pretreat = OpenStudio::Model::SetpointManagerOutdoorAirPretreat.new(model)
spm_oa_pretreat.setMinimumSetpointTemperature(-99.0)
spm_oa_pretreat.setMaximumSetpointTemperature(99.0)
spm_oa_pretreat.setMinimumSetpointHumidityRatio(0.00001)
spm_oa_pretreat.setMaximumSetpointHumidityRatio(1.0)
- # Reference setpoint node and
- # Mixed air stream node are outlet
+ # Reference setpoint node and
+ # Mixed air stream node are outlet
# node of the OA system
mixed_air_node = oa_system.mixedAirModelObject.get.to_Node.get
spm_oa_pretreat.setReferenceSetpointNode(mixed_air_node)
spm_oa_pretreat.setMixedAirStreamNode(mixed_air_node)
# Outdoor air node is
@@ -1713,65 +1732,65 @@
erv_outlet = erv.primaryAirOutletModelObject.get.to_Node.get
spm_oa_pretreat.addToNode(erv_outlet)
# Apply the prototype Heat Exchanger power assumptions.
erv.setPrototypeNominalElectricPower
-
+
return true
-
- end
-
+
+ end
+
# Determine if multizone vav optimization is required.
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
- # @todo Add exception logic for
+ # @return [Bool] Returns true if required, false if not.
+ # @todo Add exception logic for
# systems with AIA healthcare ventilation requirements
# dual duct systems
def is_multizone_vav_optimization_required(template, climate_zone)
multizone_opt_required = false
-
+
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
-
+
# Not required before 90.1-2010
return multizone_opt_required
-
+
when '90.1-2010', '90.1-2013'
-
+
# Not required for systems with fan-powered terminals
num_fan_powered_terminals = 0
self.demandComponents.each do |comp|
- if comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized || comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
+ 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::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, multizone vav optimization is not required because the system has #{num_fan_powered_terminals} fan-powered terminals.")
return multizone_opt_required
end
-
+
# Not required for systems that require an ERV
if self.has_energy_recovery
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: multizone vav optimization is not required because the system has Energy Recovery.")
return multizone_opt_required
end
-
+
# Get the OA intake
controller_oa = nil
controller_mv = nil
oa_system = nil
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
controller_mv = controller_oa.controllerMechanicalVentilation
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.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 self.designSupplyAirFlowRate.is_initialized
dsn_flow_m3_per_s = self.designSupplyAirFlowRate.get
elsif self.autosizedDesignSupplyAirFlowRate.is_initialized
@@ -1779,11 +1798,11 @@
else
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.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
@@ -1791,14 +1810,14 @@
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::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because system is more than 70% OA.")
return multizone_opt_required
@@ -1807,127 +1826,129 @@
# TODO Not required for dual-duct systems
# if self.isDualDuct
# OpenStudio::logFree(OpenStudio::Warn, "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
-
- end
-
+
+ end
+
# Enable multizone vav optimization by changing the Outdoor Air Method
# in the Controller:MechanicalVentilation object to 'VentilationRateProcedure'
#
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
def enable_multizone_vav_optimization
-
+
# Enable multizone vav optimization
# at each timestep.
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
controller_mv = controller_oa.controllerMechanicalVentilation
controller_mv.setSystemOutdoorAirMethod('VentilationRateProcedure')
else
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot enable multizone vav optimization because the system has no OA intake.")
return false
end
-
- end
-
+
+ end
+
# Disable multizone vav optimization by changing the Outdoor Air Method
# in the Controller:MechanicalVentilation object to 'ZoneSum'
#
# @return [Bool] Returns true if required, false if not.
def disable_multizone_vav_optimization
-
+
# Disable multizone vav optimization
# at each timestep.
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
controller_mv = controller_oa.controllerMechanicalVentilation
controller_mv.setSystemOutdoorAirMethod('ZoneSum')
else
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot disable multizone vav optimization because the system has no OA intake.")
return false
end
-
- end
+ end
+
# Set the minimum VAV damper positions
#
- #
+ #
def set_minimum_vav_damper_positions(template)
-
+
self.thermalZones.each do |zone|
zone.equipment.each do |equip|
if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
zone_oa_per_area = zone.outdoor_airflow_rate_per_area
vav_terminal = equip.to_AirTerminalSingleDuctVAVReheat.get
vav_terminal.set_minimum_damper_position(template, zone_oa_per_area)
end
end
end
-
+
return true
-
+
end
-
+
# Adjust minimum VAV damper positions to the values
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
def adjust_minimum_vav_damper_positions
-
+
# Total uncorrected outdoor airflow rate
v_ou = 0.0
self.thermalZones.each do |zone|
v_ou += zone.outdoor_airflow_rate
end
+
v_ou_cfm = OpenStudio.convert(v_ou, 'm^3/s', 'cfm').get
-
+
# System primary airflow rate (whether autosized or hard-sized)
v_ps = 0.0
+
if self.autosizedDesignSupplyAirFlowRate.is_initialized
v_ps = self.autosizedDesignSupplyAirFlowRate.get
else
v_ps = self.designSupplyAirFlowRate.get
end
v_ps_cfm = OpenStudio.convert(v_ps, 'm^3/s', 'cfm').get
-
+
# Average outdoor air fraction
x_s = v_ou / v_ps
-
- OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
-
+
+ OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
+
# Determine the zone ventilation effectiveness
# for every zone on the system.
# When ventilation effectiveness is too low,
# increase the minimum damper position.
e_vzs = []
e_vzs_adj = []
num_zones_adj = 0
self.thermalZones.sort.each do |zone|
-
+
# Breathing zone airflow rate
- v_bz = zone.outdoor_airflow_rate
-
+ v_bz = zone.outdoor_airflow_rate
+
# Zone air distribution, assumed 1 per PNNL
- e_z = 1.0
-
+ e_z = 1.0
+
# Zone airflow rate
- v_oz = v_bz / e_z
-
+ v_oz = v_bz / e_z
+
# Primary design airflow rate
- # max of heating and cooling
+ # max of heating and cooling
# design air flow rates
v_pz = 0.0
clg_dsn_flow = zone.autosizedCoolingDesignAirFlowRate
if clg_dsn_flow.is_initialized
clg_dsn_flow = clg_dsn_flow.get
@@ -1944,11 +1965,11 @@
v_pz = htg_dsn_flow
end
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: #{zone.name} htg_dsn_flow could not be found.")
end
-
+
# Get the minimum damper position
mdp = 1.0
zone.equipment.each do |equip|
if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
term = equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.get
@@ -1964,50 +1985,50 @@
elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
term = equip.to_AirTerminalSingleDuctVAVReheat.get
mdp = term.constantMinimumAirFlowFraction
end
end
-
+
# Zone minimum discharge airflow rate
v_dz = v_pz*mdp
-
+
# Zone discharge air fraction
z_d = v_oz / v_dz
-
+
# Zone ventilation effectiveness
e_vz = 1+x_s-z_d
-
+
# Store the ventilation effectiveness
e_vzs << e_vz
-
+
OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} v_oz = #{v_oz.round(2)} m^3/s, v_pz = #{v_pz.round(2)} m^3/s, v_dz = #{v_dz.round(2)}, z_d = #{z_d.round(2)}.")
-
+
# Check the ventilation effectiveness against
# the minimum limit per PNNL and increase
# as necessary.
if e_vz < 0.6
-
+
# Adjusted discharge air fraction
z_d_adj = 1+x_s-0.6
-
+
# Adjusted min discharge airflow rate
v_dz_adj = v_oz / z_d_adj
-
+
# Adjusted minimum damper position
mdp_adj = v_dz_adj / v_pz
-
+
# Don't allow values > 1
if mdp_adj > 1.0
mdp_adj = 1.0
end
-
+
# Zone ventilation effectiveness
e_vz_adj = 1+x_s-z_d_adj
-
+
# Store the ventilation effectiveness
e_vzs_adj << e_vz_adj
-
+
# Set the adjusted minimum damper position
zone.equipment.each do |equip|
if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
term = equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.get
term.setZoneMinimumAirFlowFraction(mdp_adj)
@@ -2020,50 +2041,50 @@
elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
term = equip.to_AirTerminalSingleDuctVAVReheat.get
term.setConstantMinimumAirFlowFraction(mdp_adj)
end
end
-
+
num_zones_adj += 1
-
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} has a ventilation effectiveness of #{e_vz.round(2)}. Increasing to #{e_vz_adj.round(2)} by increasing minimum damper position from #{mdp.round(2)} to #{mdp_adj.round(2)}.")
else
# Store the unadjusted value
e_vzs_adj << e_vz
end
-
+
end
-
+
# Min system zone ventilation effectiveness
e_v = e_vzs.min
-
- # Total system outdoor intake flow rate
+
+ # Total system outdoor intake flow rate
v_ot = v_ou / e_v
v_ot_cfm = OpenStudio.convert(v_ot, 'm^3/s', 'cfm').get
-
+
# Min system zone ventilation effectiveness
e_v_adj = e_vzs_adj.min
-
- # Total system outdoor intake flow rate
+
+ # Total system outdoor intake flow rate
v_ot_adj = v_ou / e_v_adj
v_ot_adj_cfm = OpenStudio.convert(v_ot_adj, 'm^3/s', 'cfm').get
-
+
# Report out the results of the multizone calculations
if num_zones_adj > 0
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone outdoor air requirements gives a value of #{v_ou_cfm.round} cfm. Applying the multizone method gives a value of #{v_ot_cfm.round} cfm, with an original system ventilation effectiveness of #{e_v.round(2)}. After increasing the minimum damper position in #{num_zones_adj} critical zones, the resulting requirement is #{v_ot_adj_cfm.round} cfm with a system ventilation effectiveness of #{e_v_adj.round(2)}.")
else
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone requirements gives a value of #{v_ou_cfm.round} cfm. However, applying the multizone method requires #{v_ot_adj_cfm.round} cfm based on the ventilation effectiveness of the system.")
end
-
+
# Hard-size the sizing:system
# object with the calculated min OA flow rate
sizing_system = self.sizingSystem
sizing_system.setDesignOutdoorAirFlowRate(v_ot_adj)
-
+
return true
-
+
end
# For critical zones of Outpatient, if the minimum airflow rate required by the accreditation standard (AIA 2001) is significantly
# less than the autosized peak design airflow in any of the three climate zones (Houston, Baltimore and Burlington), the minimum
# airflow fraction of the terminal units is reduced to the value: "required minimum airflow rate / autosized peak design flow"
@@ -2096,34 +2117,34 @@
end
end
end
return true
end
-
+
# Determine if demand control ventilation (DCV) is
# required for this air loop.
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
- # @todo Add exception logic for
+ # @return [Bool] Returns true if required, false if not.
+ # @todo Add exception logic for
# systems that serve multifamily, parking garage, warehouse
def is_demand_control_ventilation_required(template, climate_zone)
-
+
dcv_required = false
-
+
# Not required by the old vintages
- if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004'
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required for any system.")
+ if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004' || template == 'NECB 2011'
+ OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{template} #{climate_zone}: #{self.name}: DCV is not required for any system.")
return dcv_required
end
-
+
# Not required for systems that require an ERV
if self.has_energy_recovery
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has Energy Recovery.")
return dcv_required
end
-
+
# Area, occupant density, and OA flow limits
min_area_ft2 = 0
min_occ_per_1000_ft2 = 0
min_oa_without_economizer_cfm = 0
min_oa_with_economizer_cfm = 0
@@ -2142,54 +2163,54 @@
min_area_ft2 = 500
min_occ_per_1000_ft2 = 25
min_oa_without_economizer_cfm = 3000
min_oa_with_economizer_cfm = 750
end
-
+
# Get the area served and the number of occupants
area_served_m2 = 0
num_people = 0
self.thermalZones.each do |zone|
zone.spaces.each do |space|
area_served_m2 += space.floorArea
num_people += space.numberOfPeople
end
end
-
+
# Check the minimum area
area_served_ft2 = OpenStudio.convert(area_served_m2, 'm^2', 'ft^2').get
if area_served_ft2 < min_area_ft2
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system serves #{area_served_ft2.round} ft2, but the minimum size is #{min_area_ft2.round} ft2.")
return dcv_required
end
-
+
# Check the minimum occupancy density
occ_per_ft2 = num_people / area_served_ft2
- occ_per_1000_ft2 = occ_per_ft2*1000
-
+ occ_per_1000_ft2 = occ_per_ft2*1000
+
if occ_per_1000_ft2 < min_occ_per_1000_ft2
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system occupant density is #{occ_per_1000_ft2.round} people/1000 ft2, but the minimum occupant density is #{min_occ_per_1000_ft2.round} people/1000 ft2.")
return dcv_required
end
-
- # Get the min OA flow rate
+
+ # Get the min OA flow rate
oa_flow_m3_per_s = 0
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
end
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, DCV not applicable because it has no OA intake.")
return dcv_required
end
oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
-
-
+
+
# Check for min OA without an economizer OR has economizer
if oa_flow_cfm < min_oa_without_economizer_cfm && self.has_economizer == false
# Message if doesn't pass OA limit
if oa_flow_cfm < min_oa_without_economizer_cfm
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_without_economizer_cfm.round} cfm.")
@@ -2204,61 +2225,61 @@
# If has economizer, cfm limit is lower
if oa_flow_cfm < min_oa_with_economizer_cfm && self.has_economizer
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has an economizer, but the min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_with_economizer_cfm.round} cfm for systems with an economizer.")
return dcv_required
end
-
+
# If here, DCV is required
dcv_required = true
-
+
return dcv_required
-
- end
+ end
+
# Enable demand control ventilation (DCV) for this air loop.
#
# @return [Bool] Returns true if required, false if not.
def enable_demand_control_ventilation()
-
+
# Get the OA intake
controller_oa = nil
controller_mv = nil
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
controller_mv = controller_oa.controllerMechanicalVentilation
if controller_mv.demandControlledVentilation == true
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV was already enabled.")
return true
end
else
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Could not enable DCV since the system has no OA intake.")
return false
end
-
+
# Change the min flow rate in the controller outdoor air
controller_oa.setMinimumOutdoorAirFlowRate(0.0)
-
+
# Enable DCV in the controller mechanical ventilation
controller_mv.setDemandControlledVentilation(true)
return true
end
-
+
# Determine if the system required supply air temperature
# (SAT) reset.
#
# @param (see #is_economizer_required)
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
def is_supply_air_temperature_reset_required(template, climate_zone)
-
+
is_sat_reset_required = false
-
+
# Only required for multizone VAV systems
return is_sat_reset_required unless self.is_multizone_vav_system
-
+
# Not required until 90.1-2010
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
return is_sat_reset_required
when '90.1-2010', '90.1-2013'
@@ -2282,15 +2303,15 @@
'ASHRAE 169-2006-7A',
'ASHRAE 169-2006-7B',
'ASHRAE 169-2006-8A',
'ASHRAE 169-2006-8B'
is_sat_reset_required = true
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is required.")
+ OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is required.")
return is_sat_reset_required
end
end
-
+
end
# Enable supply air temperature (SAT) reset based
# on the cooling demand of the warmest zone.
#
@@ -2339,129 +2360,129 @@
# on outdoor air conditions. SAT will be kept at the
# current design temperature when outdoor air is above 70F,
# increased by 5F when outdoor air is below 50F, and reset
# linearly when outdoor air is between 50F and 70F.
#
- # @return [Bool] Returns true if successful, false if not.
+ # @return [Bool] Returns true if successful, false if not.
def enable_supply_air_temperature_reset_outdoor_temperature()
-
+
# for AHU1 in Outpatient, SAT is 52F constant, no reset
return true if self.name.get == 'PVAV Outpatient F1'
-
- # Get the current setpoint and calculate
+
+ # Get the current setpoint and calculate
# the new setpoint.
sizing_system = self.sizingSystem
sat_at_hi_oat_c = sizing_system.centralCoolingDesignSupplyAirTemperature
sat_at_hi_oat_f = OpenStudio.convert(sat_at_hi_oat_c, 'C', 'F').get
# 5F increase when it's cold outside,
# and therefore less cooling capacity is likely required.
increase_f = 5.0
sat_at_lo_oat_f = sat_at_hi_oat_f+increase_f
sat_at_lo_oat_c = OpenStudio.convert(sat_at_lo_oat_f, 'F', 'C').get
-
+
# Define the high and low outdoor air temperatures
lo_oat_f = 50
lo_oat_c = OpenStudio.convert(lo_oat_f, 'F', 'C').get
hi_oat_f = 70
hi_oat_c = OpenStudio.convert(hi_oat_f, 'F', 'C').get
-
+
# Create a setpoint manager
sat_oa_reset = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model)
sat_oa_reset.setName("#{self.name} SAT Reset")
sat_oa_reset.setControlVariable('Temperature')
sat_oa_reset.setSetpointatOutdoorLowTemperature(sat_at_lo_oat_c)
sat_oa_reset.setOutdoorLowTemperature(lo_oat_c)
sat_oa_reset.setSetpointatOutdoorHighTemperature(sat_at_hi_oat_c)
sat_oa_reset.setOutdoorHighTemperature(hi_oat_c)
-
+
# Attach the setpoint manager to the
# supply outlet node of the system.
sat_oa_reset.addToNode(self.supplyOutletNode)
-
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset was enabled. When OAT > #{hi_oat_f.round}F, SAT is #{sat_at_hi_oat_f.round}F. When OAT < #{lo_oat_f.round}F, SAT is #{sat_at_lo_oat_f.round}F. It varies linearly in between these points.")
-
+
return true
-
+
end
-
+
# Determine if the system has an economizer
#
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
def has_economizer()
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
economizer_type = oa_control.getEconomizerControlType
-
+
# Return false if no economizer is present
if economizer_type == 'NoEconomizer'
return false
else
return true
end
-
+
end
-
+
# Determine if the system is a multizone VAV system
#
- # @return [Bool] Returns true if required, false if not.
+ # @return [Bool] Returns true if required, false if not.
def is_multizone_vav_system()
-
+
is_multizone_vav_system = false
-
+
# Must serve more than 1 zone
if self.thermalZones.size < 2
return is_multizone_vav_system
end
-
+
# Must be a variable volume system
has_vav_fan = false
self.supplyComponents.each do |comp|
if comp.to_FanVariableVolume.is_initialized
has_vav_fan = true
end
end
if has_vav_fan == false
return is_multizone_vav_system
end
-
+
# If here, it's a multizone VAV system
is_multizone_vav_system = true
-
+
return is_multizone_vav_system
end
-
+
# Determine if the system has energy recovery already
#
- # @return [Bool] Returns true if an ERV is present, false if not.
+ # @return [Bool] Returns true if an ERV is present, false if not.
def has_energy_recovery()
-
+
has_erv = false
-
+
# Get the OA system
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return has_erv # No OA system
- end
-
+ end
+
# Find any ERV on the OA system
oa_sys.oaComponents.each do |oa_comp|
if oa_comp.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized
has_erv = true
end
end
-
+
return has_erv
end
# Set the VAV damper control to single maximum or
@@ -2469,65 +2490,65 @@
#
# @return [Bool] Returns true if successful, false if not
# @todo see if this impacts the sizing run.
def set_vav_damper_action(template)
damper_action = nil
- case template
+ case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', 'NECB 2011'
damper_action = 'Single Maximum'
when '90.1-2007', '90.1-2010', '90.1-2013'
damper_action = 'Dual Maximum'
end
-
+
# Interpret this as an EnergyPlus input
damper_action_eplus = nil
if damper_action == 'Single Maximum'
damper_action_eplus = 'Normal'
elsif damper_action == 'Dual Maximum'
damper_action_eplus = 'Reverse'
end
-
+
# Set the control for any VAV reheat terminals
# on this airloop.
self.demandComponents.each do |equip|
if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
term = equip.to_AirTerminalSingleDuctVAVReheat.get
term.setDamperHeatingAction(damper_action_eplus)
end
- end
-
+ end
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: VAV damper action was set to #{damper_action} control.")
-
+
return true
-
+
end
# Determine if a motorized OA damper is required
def is_motorized_oa_damper_required(template, climate_zone)
-
+
motorized_oa_damper_required = false
if self.name.to_s.include? "Outpatient F1"
motorized_oa_damper_required = true
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: always has a damper, the minimum OA schedule is the same as airloop availability schedule.")
return motorized_oa_damper_required
end
-
+
# If the system has an economizer, it must have
# a motorized damper.
if self.has_economizer
motorized_oa_damper_required = true
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Because the system has an economizer, it requires a motorized OA damper.")
return motorized_oa_damper_required
end
# Determine the exceptions based on
- # number of stories, climate zone, and
+ # number of stories, climate zone, and
# outdoor air intake rates.
minimum_oa_flow_cfm = 0
maximum_stories = 0
- case template
+ case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
# Assuming that older buildings always
# used backdraft gravity dampers
return motorized_oa_damper_required
when '90.1-2004', '90.1-2007'
@@ -2559,121 +2580,121 @@
else
minimum_oa_flow_cfm = 300
maximum_stories = 0
end
end
-
+
# Get the number of stories
num_stories = self.model.getBuildingStorys.size
-
+
# Check the number of stories exception,
# which is climate-zone dependent.
if num_stories < maximum_stories
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the building has #{num_stories} stories, less than the maximum of #{maximum_stories} stories for climate zone #{climate_zone}.")
return motorized_oa_damper_required
- end
-
- # Get the min OA flow rate
+ end
+
+ # Get the min OA flow rate
oa_flow_m3_per_s = 0
if self.airLoopHVACOutdoorAirSystem.is_initialized
oa_system = self.airLoopHVACOutdoorAirSystem.get
- controller_oa = oa_system.getControllerOutdoorAir
+ controller_oa = oa_system.getControllerOutdoorAir
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
end
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, Motorized OA damper not applicable because it has no OA intake.")
return motorized_oa_damper_required
end
- oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
-
+ oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
+
# Check the OA flow rate exception
if oa_flow_cfm < minimum_oa_flow_cfm
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the system OA intake of #{oa_flow_cfm.round} cfm is less than the minimum threshold of #{minimum_oa_flow_cfm} cfm.")
return motorized_oa_damper_required
end
# If here, motorized damper is required
motorized_oa_damper_required = true
-
+
return motorized_oa_damper_required
end
-
+
# Add a motorized damper by modifying the OA schedule
# to require zero OA during unoccupied hours. This means
# that even during morning warmup or nightcyling, no OA will
# be brought into the building, lowering heating/cooling load.
# If no occupancy schedule is supplied, one will be created.
# In this case, occupied is defined as the total percent
# occupancy for the loop for all zones served.
- #
+ #
# @param min_occ_pct [Double] the fractional value below which
# the system will be considered unoccupied.
# @param occ_sch [OpenStudio::Model::Schedule] the occupancy schedule.
# If not supplied, one will be created based on the supplied
# occupancy threshold.
- # @return [Bool] true if successful, false if not
+ # @return [Bool] true if successful, false if not
def add_motorized_oa_damper(min_occ_pct = 0.15, occ_sch = nil)
-
+
# Get the airloop occupancy schedule if none supplied
if occ_sch.nil?
occ_sch = self.get_occupancy_schedule(min_occ_pct)
flh = occ_sch.annual_equivalent_full_load_hrs
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used to close OA damper during unoccupied hours.")
else
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Setting motorized OA damper schedule to #{occ_sch.name}.")
end
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
-
+
# Set the minimum OA schedule to follow occupancy
- oa_control.setMinimumOutdoorAirSchedule(occ_sch)
-
+ oa_control.setMinimumOutdoorAirSchedule(occ_sch)
+
return true
-
- end
-
+
+ end
+
# Remove a motorized OA damper by modifying the OA schedule
# to require full OA at all times. Whenever the fan operates,
# the damper will be open and OA will be brought into the building.
# This reflects the use of a backdraft gravity damper, and
# increases building loads unnecessarily during unoccupied hours.
def remove_motorized_oa_damper
-
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
-
+
# Set the minimum OA schedule to always 1 (100%)
oa_control.setMinimumOutdoorAirSchedule(self.model.alwaysOnDiscreteSchedule)
return true
-
- end
+ end
+
# This method creates a schedule where the value is zero when
# the overall occupancy for all zones on the airloop is below
# the specified threshold, and one when the overall occupancy is
# greater than or equal to the threshold. This method is designed
# to use the total number of people on the airloop, so if there is
# a zone that is continuously occupied by a few people, but other
- # zones that are intermittently occupied by many people, the
+ # zones that are intermittently occupied by many people, the
# first zone doesn't drive the entire system.
#
# @param occupied_percentage_threshold [Double] the minimum fraction (0 to 1) that counts as occupied
# @return [ScheduleRuleset] a ScheduleRuleset where 0 = unoccupied, 1 = occupied
# @todo Speed up this method. Bottleneck is ScheduleRule.getDaySchedules
@@ -2697,11 +2718,11 @@
next if num_ppl_sch.empty? # Skip non-ruleset schedules
num_ppl_sch = num_ppl_sch.get
num_ppl = people.getNumberOfPeople(space.floorArea)
if occ_schedules_num_occ[num_ppl_sch].nil?
occ_schedules_num_occ[num_ppl_sch] = num_ppl
- max_occ_on_airloop += num_ppl
+ max_occ_on_airloop += num_ppl
else
occ_schedules_num_occ[num_ppl_sch] += num_ppl
max_occ_on_airloop += num_ppl
end
end
@@ -2716,26 +2737,26 @@
next if num_ppl_sch.empty? # Skip non-ruleset schedules
num_ppl_sch = num_ppl_sch.get
num_ppl = people.getNumberOfPeople(space.floorArea)
if occ_schedules_num_occ[num_ppl_sch].nil?
occ_schedules_num_occ[num_ppl_sch] = num_ppl
- max_occ_on_airloop += num_ppl
+ max_occ_on_airloop += num_ppl
else
occ_schedules_num_occ[num_ppl_sch] += num_ppl
max_occ_on_airloop += num_ppl
end
end
end
end
end
-
+
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{self.name} has #{occ_schedules_num_occ.size} unique occ schedules.")
occ_schedules_num_occ.each do |occ_sch, num_occ|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " #{occ_sch.name} - #{num_occ.round} people")
end
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Total #{max_occ_on_airloop.round} people on #{self.name}")
-
+
# For each day of the year, determine
#time_value_pairs = []
year = self.model.getYearDescription
yearly_data = []
yearly_times = OpenStudio::DateTimeVector.new
@@ -2743,25 +2764,25 @@
for i in 1..365
times_on_this_day = []
os_date = year.makeDate(i)
day_of_week = os_date.dayOfWeek.valueName
-
+
# Get the unique time indices and corresponding day schedules
occ_schedules_day_schs = {}
day_sch_num_occ = {}
occ_schedules_num_occ.each do |occ_sch, num_occ|
-
+
# Get the day schedules for this day
# (there should only be one)
day_schs = occ_sch.getDaySchedules(os_date, os_date)
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "Schedule #{occ_sch.name} has #{day_schs.size} day schs") unless day_schs.size == 1
day_schs[0].times.each do |time|
times_on_this_day << time.toString
end
day_sch_num_occ[day_schs[0]] = num_occ
-
+
end
# Determine the total fraction for the airloop at each time
daily_times = []
daily_os_times = []
@@ -2775,11 +2796,11 @@
tot_occ_at_time = 0
day_sch_num_occ.each do |day_sch, num_occ|
occ_frac = day_sch.getValue(os_time)
tot_occ_at_time += occ_frac * num_occ
end
-
+
# Total fraction for the airloop at each time
air_loop_occ_frac = tot_occ_at_time / max_occ_on_airloop
occ_status = 0 # unoccupied
if air_loop_occ_frac >= occupied_percentage_threshold
occ_status = 1
@@ -2792,39 +2813,39 @@
daily_occs << air_loop_occ_frac.round(2)
end
# OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{daily_times.join(', ')} #{daily_values.join(', ')}")
-
+
# Simplify the daily times to eliminate intermediate
# points with the same value as the following point.
simple_daily_times = []
simple_daily_os_times = []
simple_daily_values = []
simple_daily_occs = []
- daily_values.each_with_index do |value, i|
+ daily_values.each_with_index do |value, i|
next if value == daily_values[i+1]
simple_daily_times << daily_times[i]
simple_daily_os_times << daily_os_times[i]
simple_daily_values << daily_values[i]
simple_daily_occs << daily_occs[i]
end
-
+
# OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{simple_daily_times.join(', ')} {simple_daily_values.join(', ')}")
-
+
# Store the daily values
yearly_data << {'date'=>os_date,'day_of_week'=>day_of_week,'times'=>simple_daily_times,'values'=>simple_daily_values,'daily_os_times'=>simple_daily_os_times, 'daily_occs'=>simple_daily_occs}
end
-
+
# Create a TimeSeries from the data
#time_series = OpenStudio::TimeSeries.new(times, values, 'unitless')
# Make a schedule ruleset
sch_name = "#{self.name} Occ Sch"
sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(self.model)
- sch_ruleset.setName("#{sch_name}")
+ sch_ruleset.setName("#{sch_name}")
# Default - All Occupied
day_sch = sch_ruleset.defaultDaySchedule
day_sch.setName("#{sch_name} Default")
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
@@ -2832,19 +2853,19 @@
# Winter Design Day - All Occupied
day_sch = OpenStudio::Model::ScheduleDay.new(self.model)
sch_ruleset.setWinterDesignDaySchedule(day_sch)
day_sch = sch_ruleset.winterDesignDaySchedule
day_sch.setName("#{sch_name} Winter Design Day")
- day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
+ day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
# Summer Design Day - All Occupied
day_sch = OpenStudio::Model::ScheduleDay.new(self.model)
sch_ruleset.setSummerDesignDaySchedule(day_sch)
day_sch = sch_ruleset.summerDesignDaySchedule
day_sch.setName("#{sch_name} Summer Design Day")
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
-
+
# Create ruleset schedules, attempting to create
# the minimum number of unique rules.
['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'].each do |day_of_week|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{day_of_week}")
end_of_prev_rule = yearly_data[0]['date']
@@ -2855,25 +2876,25 @@
next unless day == day_of_week
date = daily_data['date']
times = daily_data['times']
values = daily_data['values']
daily_occs = daily_data['daily_occs']
-
+
# If the next (Monday, Tuesday, etc.)
# is the same as today, keep going.
# If the next is different, or if
# we've reached the end of the year,
# create a new rule
if !yearly_data[i+7].nil?
next_day_times = yearly_data[i+7]['times']
next_day_values = yearly_data[i+7]['values']
next if times == next_day_times && values == next_day_values
end
-
+
daily_os_times = daily_data['daily_os_times']
daily_occs = daily_data['daily_occs']
-
+
# If here, we need to make a rule to cover from the previous
# rule to today
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "Making a new rule for #{day_of_week} from #{end_of_prev_rule.to_s} to #{date}")
sch_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset)
sch_rule.setName("#{sch_name} #{day_of_week} Rule")
@@ -2883,11 +2904,11 @@
value = values[i]
next if value == values[i+1] # Don't add breaks if same value
day_sch.addValue(time, value)
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Adding value #{time}, #{value}")
end
-
+
# Set the dates when the rule applies
sch_rule.setStartDate(end_of_prev_rule)
sch_rule.setEndDate(date)
# Individual Days
@@ -2896,84 +2917,84 @@
sch_rule.setApplyWednesday(true) if day_of_week == 'Wednesday'
sch_rule.setApplyThursday(true) if day_of_week == 'Thursday'
sch_rule.setApplyFriday(true) if day_of_week == 'Friday'
sch_rule.setApplySaturday(true) if day_of_week == 'Saturday'
sch_rule.setApplySunday(true) if day_of_week == 'Sunday'
-
+
# Reset the previous rule end date
end_of_prev_rule = date + OpenStudio::Time.new(0, 24, 0, 0)
-
+
end
-
+
end
return sch_ruleset
-
- end
-
+
+ end
+
# Generate the EMS used to implement the economizer
# and staging controls for packaged single zone units.
# @note The resulting EMS doesn't actually get added to
# the IDF yet.
#
def apply_single_zone_controls(template, climate_zone)
-
+
# Number of stages is determined by the template
num_stages = nil
- case template
+ case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'NECB 2011'
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No special economizer controls were modeled.")
return true
when '90.1-2004', '90.1-2007'
num_stages = 1
when '90.1-2010', '90.1-2013'
num_stages = 2
end
-
+
# Scrub special characters from the system name
sn = self.name.get.to_s
snc = sn.gsub(/\W/,'').gsub('_','')
-
+
# Get the zone name
zone = self.thermalZones[0]
zone_name = zone.name.get.to_s
zn_name_clean = zone_name.gsub(/\W/,'_')
-
+
# Zone air node
- zone_air_node_name = zone.zoneAirNode.name.get
-
+ zone_air_node_name = zone.zoneAirNode.name.get
+
# Get the OA system and OA controller
oa_sys = self.airLoopHVACOutdoorAirSystem
if oa_sys.is_initialized
oa_sys = oa_sys.get
else
return false # No OA system
end
oa_control = oa_sys.getControllerOutdoorAir
oa_control_name = oa_control.name.get
oa_node_name = oa_sys.outboardOANode.get.name.get
-
+
# Get the name of the min oa schedule
min_oa_sch_name = nil
if oa_control.minimumOutdoorAirSchedule.is_initialized
min_oa_sch_name = oa_control.minimumOutdoorAirSchedule.get.name.get
else
min_oa_sch_name = self.model.alwaysOnDiscreteSchedule.name.get
end
-
+
# Get the supply fan
if self.supplyFan.empty?
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No supply fan found, cannot apply DX fan/economizer control.")
return false
end
fan = self.supplyFan.get
fan_name = fan.name.get
-
+
# Supply outlet node
sup_out_node = self.supplyOutletNode
sup_out_node_name = sup_out_node.name.get
-
+
# DX Cooling Coil
dx_coil = nil
self.supplyComponents.each do |equip|
if equip.to_CoilCoolingDXSingleSpeed.is_initialized
dx_coil = equip.to_CoilCoolingDXSingleSpeed.get
@@ -2985,11 +3006,11 @@
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No DX cooling coil found, cannot apply DX fan/economizer control.")
return false
end
dx_coil_name = dx_coil.name.get
dx_coilsys_name = "#{dx_coil_name} CoilSystem"
-
+
# Heating Coil
htg_coil = nil
self.supplyComponents.each do |equip|
if equip.to_CoilHeatingGas.is_initialized
htg_coil = equip.to_CoilHeatingGas.get
@@ -3002,61 +3023,61 @@
if htg_coil.nil?
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No heating coil found, cannot apply DX fan/economizer control.")
return false
end
htg_coil_name = htg_coil.name.get
-
+
# Create an economizer maximum OA fraction schedule with
# a maximum of 70% to reflect damper leakage per PNNL
max_oa_sch_name = "#{snc}maxOASch"
max_oa_sch = OpenStudio::Model::ScheduleRuleset.new(self.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.7)
oa_control.setMaximumFractionofOutdoorAirSchedule(max_oa_sch)
-
+
ems = "
-
+
! Sensors
-
+
EnergyManagementSystem:Sensor,
- #{snc}OASch,
- #{min_oa_sch_name}, !- Output:Variable or Output:Meter Index Key Name,
+ #{snc}OASch,
+ #{min_oa_sch_name}, !- Output:Variable or Output:Meter Index Key Name,
Schedule Value; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
#{zn_name_clean}Temp,
#{zone_air_node_name}, !- Output:Variable or Output:Meter Index Key Name
System Node Temperature; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}OAFlowMass,
+ #{snc}OAFlowMass,
#{oa_node_name}, !- Output:Variable or Output:Meter Index Key Name
System Node Mass Flow Rate; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}HeatingRTF,
+ #{snc}HeatingRTF,
#{htg_coil_name}, !- Output:Variable or Output:Meter Index Key Name
Heating Coil Runtime Fraction; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}RTF,
+ #{snc}RTF,
#{dx_coil_name}, !- Output:Variable or Output:Meter Index Key Name
Cooling Coil Runtime Fraction; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}SpeedRatio,
+ #{snc}SpeedRatio,
#{dx_coilsys_name}, !- Output:Variable or Output:Meter Index Key Name
Coil System Compressor Speed Ratio; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}DATRqd,
+ #{snc}DATRqd,
#{sup_out_node_name}, !- Output:Variable or Output:Meter Index Key Name
System Node Setpoint Temperature; !- Output:Variable or Output:Meter Name
EnergyManagementSystem:Sensor,
- #{snc}EconoStatus,
+ #{snc}EconoStatus,
#{sn}, !- Output:Variable or Output:Meter Index Key Name
Air System Outdoor Air Economizer Status; !- Output:Variable or Output:Meter Name
! Internal Variables
@@ -3064,23 +3085,23 @@
#{snc}FanDesignPressure,
#{fan_name}, !- Internal Data Index Key Name
Fan Nominal Pressure Rise; !- Internal Data Type
EnergyManagementSystem:InternalVariable,
- #{snc}DesignFlowMass,
+ #{snc}DesignFlowMass,
#{oa_control_name},!- Internal Data Index Key Name
Outdoor Air Controller Maximum Mass Flow Rate; !- Internal Data Type
EnergyManagementSystem:InternalVariable,
- #{snc}OADesignMass,
+ #{snc}OADesignMass,
#{oa_control_name},!- Internal Data Index Key Name
Outdoor Air Controller Minimum Mass Flow Rate; !- Internal Data Type
! Actuators
EnergyManagementSystem:Actuator,
- #{snc}FanPressure,
+ #{snc}FanPressure,
#{fan_name}, !- Actuated Component Unique Name
Fan, !- Actuated Component Type
Fan Pressure Rise; !- Actuated Component Control Type
EnergyManagementSystem:Actuator,
@@ -3091,130 +3112,130 @@
EnergyManagementSystem:GlobalVariable,
#{snc}FanPwrExp, !- Erl Variable 1 Name
#{snc}Stg1Spd, !- Erl Variable 2 Name
#{snc}Stg2Spd, !- Erl Variable 3 Name
- #{snc}HeatSpeed,
- #{snc}VenSpeed,
- #{snc}NumberofStages;
+ #{snc}HeatSpeed,
+ #{snc}VenSpeed,
+ #{snc}NumberofStages;
EnergyManagementSystem:Program,
#{snc}EconomizerCTRLProg,
- SET #{snc}TimestepEconEff = 0.7,
- SET #{snc}MaxE = 0.7,
+ SET #{snc}TimestepEconEff = 0.7,
+ SET #{snc}MaxE = 0.7,
SET #{snc}DATRqd = (#{snc}DATRqd*1.8)+32,
SET OATF = (OATF*1.8)+32,
SET OAwbF = (OAwbF*1.8)+32,
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
SET #{snc}EconoActive = 1,
- ELSE,
+ ELSE,
SET #{snc}EconoActive = 0,
- ENDIF,
+ ENDIF,
SET #{snc}dTNeeded = 75-#{snc}DATRqd,
SET #{snc}CoolDesdT = ((98*0.15)+(75*(1-0.15)))-55,
SET #{snc}CoolLoad = #{snc}dTNeeded/ #{snc}CoolDesdT,
- IF #{snc}CoolLoad > 1,
+ IF #{snc}CoolLoad > 1,
SET #{snc}CoolLoad = 1,
ELSEIF #{snc}CoolLoad < 0,
SET #{snc}CoolLoad = 0,
- ENDIF,
+ ENDIF,
IF #{snc}EconoActive == 1,
SET #{snc}Stage = #{snc}NumberofStages,
- IF #{snc}Stage == 2,
+ IF #{snc}Stage == 2,
IF #{snc}CoolLoad < 0.6,
SET #{snc}TimestepEconEff = #{snc}MaxE,
- ELSE,
+ ELSE,
SET #{snc}ECOEff = 0-2.18919863612305,
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.674461284910428*#{snc}CoolLoad),
SET #{snc}ECOEff = #{snc}ECOEff+(0.000459106275872404*(OATF^2)),
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.00000484778537945252*(OATF^3)),
SET #{snc}ECOEff = #{snc}ECOEff+(0.182915713033586*OAwbF),
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.00382838660261133*(OAwbF^2)),
SET #{snc}ECOEff = #{snc}ECOEff+(0.0000255567460240583*(OAwbF^3)),
SET #{snc}TimestepEconEff = #{snc}ECOEff,
- ENDIF,
- ELSE,
+ ENDIF,
+ ELSE,
SET #{snc}ECOEff = 2.36337942464462,
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.409939515512619*#{snc}CoolLoad),
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.0565205596792225*OAwbF),
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.0000632612294169389*(OATF^2)),
SET #{snc}TimestepEconEff = #{snc}ECOEff+(0.000571724868775081*(OAwbF^2)),
- ENDIF,
+ ENDIF,
IF #{snc}TimestepEconEff > #{snc}MaxE,
SET #{snc}TimestepEconEff = #{snc}MaxE,
ELSEIF #{snc}TimestepEconEff < (#{snc}OADesignMass*#{snc}OASch),
SET #{snc}TimestepEconEff = (#{snc}OADesignMass*#{snc}OASch),
- ENDIF,
- ENDIF;
+ ENDIF,
+ ENDIF;
EnergyManagementSystem:Program,
- #{snc}SetFanPar,
- IF #{snc}NumberofStages == 1,
- Return,
- ENDIF,
+ #{snc}SetFanPar,
+ IF #{snc}NumberofStages == 1,
+ Return,
+ ENDIF,
SET #{snc}FanPwrExp = 2.2,
SET #{snc}OAFrac = #{snc}OAFlowMass/#{snc}DesignFlowMass,
IF #{snc}OAFrac < 0.66,
SET #{snc}VenSpeed = 0.66,
SET #{snc}Stg1Spd = 0.66,
- ELSE,
+ ELSE,
SET #{snc}VenSpeed = #{snc}OAFrac,
SET #{snc}Stg1Spd = #{snc}OAFrac,
- ENDIF,
+ ENDIF,
SET #{snc}Stg2Spd = 1.0,
SET #{snc}HeatSpeed = 1.0;
EnergyManagementSystem:Program,
- #{snc}FanControl,
- IF #{snc}NumberofStages == 1,
- Return,
- ENDIF,
+ #{snc}FanControl,
+ IF #{snc}NumberofStages == 1,
+ Return,
+ ENDIF,
IF #{snc}HeatingRTF > 0,
SET #{snc}Heating = #{snc}HeatingRTF,
SET #{snc}Ven = 1-#{snc}HeatingRTF,
- SET #{snc}Eco = 0,
- SET #{snc}Stage1 = 0,
- SET #{snc}Stage2 = 0,
- ELSE,
+ SET #{snc}Eco = 0,
+ SET #{snc}Stage1 = 0,
+ SET #{snc}Stage2 = 0,
+ ELSE,
SET #{snc}Heating = 0,
SET #{snc}EcoSpeed = #{snc}VenSpeed,
IF #{snc}SpeedRatio == 0,
- IF #{snc}RTF > 0,
+ IF #{snc}RTF > 0,
SET #{snc}Stage1 = #{snc}RTF,
- SET #{snc}Stage2 = 0,
+ SET #{snc}Stage2 = 0,
SET #{snc}Ven = 1-#{snc}RTF,
- SET #{snc}Eco = 0,
+ SET #{snc}Eco = 0,
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
SET #{snc}Stg1Spd = 1.0,
- ENDIF,
- ELSE,
- SET #{snc}Stage1 = 0,
- SET #{snc}Stage2 = 0,
+ ENDIF,
+ ELSE,
+ SET #{snc}Stage1 = 0,
+ SET #{snc}Stage2 = 0,
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
- SET #{snc}Eco = 1.0,
- SET #{snc}Ven = 0,
- !Calculate the expected discharge air temperature if the system runs at its low speed
+ SET #{snc}Eco = 1.0,
+ SET #{snc}Ven = 0,
+ !Calculate the expected discharge air temperature if the system runs at its low speed
SET #{snc}ExpDAT = #{snc}DATRqd-(1-#{snc}VenSpeed)*#{zn_name_clean}Temp,
SET #{snc}ExpDAT = #{snc}ExpDAT/#{snc}VenSpeed,
IF OATF > #{snc}ExpDAT,
SET #{snc}EcoSpeed = #{snc}Stg2Spd,
- ENDIF,
- ELSE,
- SET #{snc}Eco = 0,
- SET #{snc}Ven = 1.0,
- ENDIF,
- ENDIF,
- ELSE,
+ ENDIF,
+ ELSE,
+ SET #{snc}Eco = 0,
+ SET #{snc}Ven = 1.0,
+ ENDIF,
+ ENDIF,
+ ELSE,
SET #{snc}Stage1 = 1-#{snc}SpeedRatio,
SET #{snc}Stage2 = #{snc}SpeedRatio,
- SET #{snc}Ven = 0,
- SET #{snc}Eco = 0,
+ SET #{snc}Ven = 0,
+ SET #{snc}Eco = 0,
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
SET #{snc}Stg1Spd = 1.0,
- ENDIF,
- ENDIF,
- ENDIF,
+ ENDIF,
+ ENDIF,
+ ENDIF,
! For each mode, (percent time in mode)*(fanSpeer^PwrExp) is the contribution to weighted fan power over time step
SET #{snc}FPR = #{snc}Ven*(#{snc}VenSpeed ^ #{snc}FanPwrExp),
SET #{snc}FPR = #{snc}FPR+#{snc}Eco*(#{snc}EcoSpeed^#{snc}FanPwrExp),
SET #{snc}FPR1 = #{snc}Stage1*(#{snc}Stg1Spd^#{snc}FanPwrExp),
SET #{snc}FPR = #{snc}FPR+#{snc}FPR1,
@@ -3233,11 +3254,11 @@
#{snc}SetNumberofStagesCallingManager,
BeginNewEnvironment,
#{snc}SetNumberofStages; !- Program Name 1
EnergyManagementSystem:ProgramCallingManager,
- #{snc}ECOManager,
+ #{snc}ECOManager,
InsideHVACSystemIterationLoop, !- EnergyPlus Model Calling Point
#{snc}EconomizerCTRLProg; !- Program Name 1
EnergyManagementSystem:ProgramCallingManager,
#{snc}FanParametermanager,
@@ -3248,23 +3269,23 @@
#{snc}FanMainManager,
BeginTimestepBeforePredictor,
#{snc}FanControl;
"
-
+
# Write the ems out
- # File.open("#{Dir.pwd}/#{snc}_ems.idf", 'w') do |file|
+ # File.open("#{Dir.pwd}/#{snc}_ems.idf", 'w') do |file|
# file.puts ems
# end
-
+
return ems
-
+
end
-
+
# Determine if static pressure reset is required for this
# system. For 90.1, this determination needs information
- # about whether or not the system has DDC control over the
+ # about whether or not the system has DDC control over the
# VAV terminals.
#
# @todo Instead of requiring the input of whether a system
# has DDC control of VAV terminals or not, determine this
# from the system itself. This may require additional information
@@ -3274,15 +3295,15 @@
# over VAV terminals.
# return [Bool] returns true if static pressure reset is required, false if not
def is_static_pressure_reset_required(template, has_ddc)
sp_reset_required = false
-
+
# A big number of btu per hr as the minimum requirement
infinity_btu_per_hr = 999999999999
minimum_capacity_btu_per_hr = infinity_btu_per_hr
-
+
# Determine the minimum capacity that requires an economizer
case template
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
# static pressure reset not required
when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
@@ -3293,48 +3314,48 @@
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: static pressure reset not required because the system does not have DDC control of VAV terminals.")
end
when 'NECB 2011'
# static pressure reset not required
end
-
+
return sp_reset_required
-
+
end
# Determine if a system's fans must shut off when
# not required.
#
# @param template [String]
# @return [Bool] true if required, false if not
def is_unoccupied_fan_shutoff_required(template)
-
+
shutoff_required = true
-
+
# Per 90.1 6.4.3.4.5, systems less than 0.75 HP
# must turn off when unoccupied.
minimum_fan_hp = nil
case template
- when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
+ when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011'
minimum_fan_hp = 0.75
end
-
+
# Determine the system fan horsepower
total_hp = 0.0
self.supply_return_exhaust_relief_fans.each do |fan|
total_hp += fan.motorHorsepower
end
-
+
# Check the HP exception
if total_hp < minimum_fan_hp
shutoff_required = false
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Unoccupied fan shutoff not required because system fan HP of #{total_hp.round(2)} HP is less than the minimum threshold of #{minimum_fan_hp} HP.")
end
return shutoff_required
-
+
end
-
+
# Shut off the system during unoccupied periods.
# During these times, systems will cycle on briefly
# if temperature drifts below setpoint. For systems
# with fan-powered terminals, only the terminal fans will
# cycle on. If the system already has a schedule other than
@@ -3345,91 +3366,91 @@
#
# @param min_occ_pct [Double] the fractional value below which
# the system will be considered unoccupied.
# @return [Bool] true if successful, false if not
def enable_unoccupied_fan_shutoff(min_occ_pct = 0.15)
-
+
# Set the system to night cycle
night_cycle_type = 'CycleOnAny'
# For VAV with PFP boxes, cycle zone fans only
if self.demandComponents('OS:AirTerminal:SingleDuct:ParallelPIU:Reheat'.to_IddObjectType).size > 0
night_cycle_type = 'CycleOnAnyZoneFansOnly'
end
self.setNightCycleControlType(night_cycle_type)
-
+
# Check if already using a schedule other than always on
avail_sch = self.availabilitySchedule
unless avail_sch == self.model.alwaysOnDiscreteSchedule
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Availability schedule is already set to #{avail_sch.name}. Will assume this includes unoccupied shut down; no changes will be made.")
return true
end
-
+
# Get the airloop occupancy schedule
loop_occ_sch = self.get_occupancy_schedule(min_occ_pct)
flh = loop_occ_sch.annual_equivalent_full_load_hrs
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used as the HVAC operation schedule.")
# Set HVAC availability schedule to follow occupancy
self.setAvailabilitySchedule(loop_occ_sch)
return true
-
+
end
-
+
# Calculate the total floor area of all zones attached
# to the air loop, in m^2.
#
# return [Double] the total floor area of all zones attached
# to the air loop, in m^2.
def floor_area_served()
-
+
total_area = 0.0
-
+
self.thermalZones.each do |zone|
total_area += zone.floorArea
end
return total_area
-
+
end
# Calculate the total floor area of all zones attached
# to the air loop that have no exterior surfaces, in m^2.
#
# return [Double] the total floor area of all zones attached
# to the air loop, in m^2.
def floor_area_served_interior_zones()
-
+
total_area = 0.0
-
+
self.thermalZones.each do |zone|
# Skip zones that have exterior surface area
next if zone.exteriorSurfaceArea > 0
total_area += zone.floorArea
end
return total_area
-
- end
-
+
+ end
+
# Calculate the total floor area of all zones attached
# to the air loop that have at least one exterior surface, in m^2.
#
# return [Double] the total floor area of all zones attached
# to the air loop, in m^2.
def floor_area_served_exterior_zones()
-
+
total_area = 0.0
-
+
self.thermalZones.each do |zone|
# Skip zones that have no exterior surface area
next if zone.exteriorSurfaceArea == 0
total_area += zone.floorArea
end
return total_area
-
+
end
# find design_supply_air_flow_rate
#
# @return [Double] design_supply_air_flow_rate m^3/s
@@ -3446,24 +3467,24 @@
end
return design_supply_air_flow_rate
end
-
+
# Determine how much data center
# area the airloop serves.
#
# @return [Double] the area of data center is served,
# in m^2.
# @todo Add an is_data_center field to the
# standards space type spreadsheet instead
# of relying on the standards space type name to
# identify a data center.
def data_center_area_served()
-
+
dc_area_m2 = 0.0
-
+
self.thermalZones.each do |zone|
zone.spaces.each do |space|
# Skip spaces with no space type
next if space.spaceType.empty?
space_type = space.spaceType.get
@@ -3472,11 +3493,11 @@
# Counts as a data center if the name includes 'data'
next unless standards_space_type.downcase.include?('data')
dc_area_m2 += space.floorArea
end
end
-
+
return dc_area_m2
-
+
end
-
+
end