Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACCA Manual S. Update #1852

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@
<sch:title>[HVACSizingControl]</sch:title>
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:BuildingSummary/h:extension/h:HVACSizingControl'>
<sch:assert role='ERROR' test='count(h:HeatPumpSizingMethodology) &lt;= 1'>Expected 0 or 1 element(s) for xpath: HeatPumpSizingMethodology</sch:assert>
<sch:assert role='ERROR' test='h:HeatPumpSizingMethodology[text()="ACCA" or text()="HERS" or text()="MaxLoad"] or not(h:HeatPumpSizingMethodology)'>Expected HeatPumpSizingMethodology to be 'ACCA' or 'HERS' or 'MaxLoad'</sch:assert>
<sch:assert role='ERROR' test='h:HeatPumpSizingMethodology[text()="ACCA2023" or text()="ACCA" or text()="HERS" or text()="MaxLoad"] or not(h:HeatPumpSizingMethodology)'>Expected HeatPumpSizingMethodology to be 'ACCA2023' or 'ACCA' or 'HERS' or 'MaxLoad'</sch:assert>
<sch:assert role='ERROR' test='count(h:HeatPumpBackupSizingMethodology) &lt;= 1'>Expected 0 or 1 element(s) for xpath: HeatPumpBackupSizingMethodology</sch:assert>
<sch:assert role='ERROR' test='h:HeatPumpBackupSizingMethodology[text()="emergency" or text()="supplemental"] or not(h:HeatPumpBackupSizingMethodology)'>Expected HeatPumpBackupSizingMethodology to be 'emergency' or 'supplemental'</sch:assert>
<sch:assert role='ERROR' test='count(h:AllowIncreasedFixedCapacities) &lt;= 1'>Expected 0 or 1 element(s) for xpath: AllowIncreasedFixedCapacities</sch:assert>
Expand Down
131 changes: 106 additions & 25 deletions HPXMLtoOpenStudio/resources/hvac_sizing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1711,26 +1711,105 @@ def self.apply_fractions_load_served(hvac_heating, hvac_loads, frac_zone_heat_lo
end

# Returns the ACCA Manual S sizing allowances for a given type of HVAC equipment.
# These sizing allowances are used in the logic that determines how to convert heating/cooling
# These sizing allowances are used in the logic that determines how to convert heating/cooling
# design loads into corresponding equipment capacities.
#
# @param hvac_cooling [HPXML::CoolingSystem or HPXML::HeatPump] The cooling portion of the current HPXML HVAC system
# @return [Array<Double, Double, Double>] Oversize fraction (frac), oversize delta (Btu/hr), undersize fraction (frac)
def self.get_hvac_size_limits(hvac_cooling)
oversize_limit = 1.15
oversize_delta = 15000.0
undersize_limit = 0.9

if not hvac_cooling.nil?
if hvac_cooling.compressor_type == HPXML::HVACCompressorTypeTwoStage
oversize_limit = 1.2
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeVariableSpeed
oversize_limit = 1.3
end
end
# New ACCA Manual S specifies different size limits for clg only/HPs depending on
# the sizing condition (Standard, Dry, Variable Speed, etc.)
# and total cooling load (24,000 BTU/hr cutoff)

return oversize_limit, oversize_delta, undersize_limit
end
# get_hvac_size_limits() does not address section/table N2.3.3 Two-Speed Heat Pump Sizing Condition
# or section/table N2.3.4 Variable-Capacity Equipment Sizing Condition
# since these size conditions also include heating size factors and minimum compressor heating size factors
# To maintain consistency w/ previous implementation in ACCA Man. S 2014, only cooling size limits are returned.
# Therefore, only sections/tables N2.3.1 and N2.3.2 from ACCA Man. S 2023 are addressed in this method.

# @param hvac_cooling [HPXML::CoolingSystem or HPXML::HeatPump] The cooling portion of the current HPXML HVAC system
# @param hvac_sizings [HVACSizingValues] Object with sizing values for a given HVAC system
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param weather [WeatherFile] Weather object containing EPW information
# @return [Array<Double, Double, Double, Double>] total clg oversize limit (frac),
# total clg undersize limit (frac), sens. clg undersize limit (frac), lat. clg undersize limit (frac)

def self.get_hvac_size_limits(hvac_cooling, hvac_sizings, hpxml_bldg, weather)

load_shr = hvac_sizings.Cool_Load_Sens / hvac_sizings.Cool_Load_Tot
# calculate Climate JSHR to determine standard vs dry sizing condition (new ACCA)

if hpxml_bldg.header.heat_pump_sizing_methodology == HPXML::HeatPumpSizingACCA2023
if not hvac_cooling.nil?
if load_shr < 0.95
# larger latent load
# Section N.2.3.1 ACCA Man. S 2023, Standard Sizing Condition
total_clg_undersize_limit = 0.90
sens_clg_undersize_limit = 0.90
lat_clg_undersize_limit = 1.00
if hvac_cooling.compressor_type == HPXML::HVACCompressorTypeSingleStage && hvac_sizings.Cool_Load_Tot <= 24000 # BTU/hr
total_clg_oversize_limit = 1.20
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeSingleStage && hvac_sizings.Cool_Load_Tot > 24000 # BTU/hr
total_clg_oversize_limit = 1.15
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeTwoStage
total_clg_oversize_limit = 1.25
end
elsif load_shr >= 0.95 # Section N.2.3.2 ACCA Man. S 2023, Dry Sizing Condition
if hvac_cooling.compressor_type == HPXML::HVACCompressorTypeSingleStage
# Total Cooling Capacity / (Total Cooling Load + 6 kBtuh) <= 1.00
# 1.00 = non-load adjusted oversize limit
# goal --> determine "load adjusted" oversize limit
# Total Cooling Capacity <= 1.00 * (Total Cooling Load + 6 kBtuh)
# Total Cooling Capacity <= 1.00 * Total Cooling Load + 1.00 * 6 kBtuh
# Total Cooling Capacity / Total Cooling Load <= 1.00 + (1.00 * 6 kBtuh) / Total Cooling Load
# Total Cooling Capacity Factor <= 1.00 + 6 kBtuh / Total Cooling Load
# Effective Total Cooling Oversize Limit = 1.00 + 6 kBtuh / Total Cooling Load
total_clg_oversize_limit = 1 + 6000 / hvac_sizings.Cool_Load_Tot
total_clg_undersize_limit = 0.90
sens_clg_undersize_limit = 0.90
lat_clg_undersize_limit = 1.00
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeTwoStage
total_clg_oversize_limit = 1.15 # minimum compressor speed! TODO: incorporate minimum speed logic elsewhere
total_clg_undersize_limit = 0.90 # total clg undersize limit for dry climate, 2-stage compressor is as specified in ACCA 2014
# ACCA 2023 Table N2.3.2 does not specify total cooling undersize limit.
sens_clg_undersize_limit = 0.90
lat_clg_undersize_limit = 1.00
end
end
end
elsif hpxml_bldg.header.heat_pump_sizing_methodology == HPXML::HeatPumpSizingACCA
# References "Overview of Size Limits for Residential HVAC Equipment" from Man. S 2014
if ((weather.data.HDD65F / weather.data.CDD50F) < 2.0) || (load_shr < 0.95)
# mild winter or has latent cooling load
if not hvac_cooling.nil?
total_clg_undersize_limit = 0.90
sens_clg_undersize_limit = 0.90
lat_clg_undersize_limit = 1.00
if hvac_cooling.compressor_type == HPXML::HVACCompressorTypeSingleStage
total_clg_oversize_limit = 1.15
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeTwoStage
total_clg_oversize_limit = 1.20
elsif hvac_cooling.compressor_type == HPXML::HVACCompressorTypeVariableSpeed
total_clg_oversize_limit = 1.30
end
end
elsif ((weather.data.HDD65F / weather.data.CDD50F) >= 2.0) && (load_shr >= 0.95)
# cold winter and no latent cooling load
# Manual S 2023 doesn't specify latent/sensible capacity undersize limits in this situation.
# from software perspective, could either return zero, nil, or the same latent/sensible
# undersize limits as the situation with latent cooling load/mild winter.
total_clg_undersize_limit = 0.90
sens_clg_undersize_limit = nil
lat_clg_undersize_limit = nil
# Max Total Cooling Capacity = Total Cooling Load + 15 kBtuh
# i.e. Total Cooling Capacity <= Total Cooling Load + 15 kBtuh
# <= means solve for oversize limit, size factor (capacity/load) <= oversize limit
# Total Cooling Capacity / Total Cooling Load <= (Total Cooling Load + 15 kBtuh) / Total Cooling Load
# Total Cooling Oversize Limit = (Total Cooling Load + 15 kBtuh) / Total Cooling Load
total_clg_oversize_limit = (hvac_sizings.Cool_Load_Tot + 15000) / hvac_sizings.Cool_Load_Tot
end
end

return total_clg_oversize_limit, total_clg_undersize_limit, sens_clg_undersize_limit, lat_clg_undersize_limit

end

# Transfers the design load totals from the HVAC loads object to the HVAC sizings object.
#
Expand Down Expand Up @@ -2108,7 +2187,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
hvac_cooling_ap = hvac_cooling.additional_properties
is_ducted = !hvac_cooling.distribution_system.nil?
cooling_delta_t = mj.cool_setpoint - hvac_cooling_ap.leaving_air_temp
oversize_limit, oversize_delta, undersize_limit = get_hvac_size_limits(hvac_cooling)
tot_clg_oversize_limit, tot_clg_undersize_limit, sens_clg_undersize_limit, lat_clg_undersize_limit = get_hvac_size_limits(hvac_cooling)
end

if hvac_sizings.Cool_Load_Tot <= 0
Expand Down Expand Up @@ -2218,11 +2297,11 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
# If the adjusted equipment size is negative (occurs at altitude), use oversize limit (the adjustment
# almost always hits the oversize limit in this case, making this a safe assumption)
if (cool_cap_design < 0) || (cool_sens_cap_design < 0)
cool_cap_design = oversize_limit * hvac_sizings.Cool_Load_Tot
cool_cap_design = tot_clg_oversize_limit * hvac_sizings.Cool_Load_Tot
end

# Limit total capacity to oversize limit
cool_cap_design = [cool_cap_design, oversize_limit * hvac_sizings.Cool_Load_Tot].min
cool_cap_design = [cool_cap_design, tot_clg_oversize_limit * hvac_sizings.Cool_Load_Tot].min

# Determine rated capacities
hvac_sizings.Cool_Capacity = cool_cap_design / total_cap_curve_value
Expand All @@ -2231,7 +2310,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
# Determine the final sensible capacity at design using the SHR
cool_sens_cap_design = cool_cap_design * design_shr

elsif cool_sens_cap_design < undersize_limit * hvac_sizings.Cool_Load_Sens
elsif cool_sens_cap_design < sens_undersize_limit * hvac_sizings.Cool_Load_Sens
# Size by MJ8 Sensible Load, return to rated conditions, find rated sensible capacity with SHRRated. Limit total
# capacity to oversizing limit.

Expand All @@ -2241,7 +2320,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
cool_cap_design = cool_sens_cap_design / design_shr

# Limit total capacity to oversize limit
cool_cap_design = [cool_cap_design, oversize_limit * hvac_sizings.Cool_Load_Tot].min
cool_cap_design = [cool_cap_design, tot_clg_oversize_limit * hvac_sizings.Cool_Load_Tot].min

# rated capacities
hvac_sizings.Cool_Capacity = cool_cap_design / total_cap_curve_value
Expand Down Expand Up @@ -2344,7 +2423,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
cool_cap_design = cool_load_sens_cap_design + cool_load_lat_cap_design

# Limit total capacity via oversizing limit
cool_cap_design = [cool_cap_design, oversize_limit * hvac_sizings.Cool_Load_Tot].min
cool_cap_design = [cool_cap_design, tot_clg_oversize_limit * hvac_sizings.Cool_Load_Tot].min
hvac_sizings.Cool_Capacity = cool_cap_design / total_cap_curve_value
hvac_sizings.Cool_Capacity_Sens = hvac_sizings.Cool_Capacity * hvac_cooling_shr
end
Expand Down Expand Up @@ -3168,8 +3247,10 @@ def self.calculate_heat_pump_adj_factor_at_outdoor_temperature(mj, hvac_heating,
# @return [Double] Heat pump backup load (Btu/hr)
def self.calculate_heat_pump_backup_load(mj, hvac_heating, heating_load, hp_nominal_heating_capacity, hvac_heating_speed, hpxml_bldg)
if hpxml_bldg.header.heat_pump_backup_sizing_methodology == HPXML::HeatPumpBackupSizingEmergency
# Size backup to meet full design load in case heat pump fails
return heating_load
# Size backup to meet 85% of design load in case heat pump fails
# New ACCA Man S (2024)--> emergency heating load is 85% of heating load
# See Table N1.16.3.2 Electric Resistance Emergency Heat
return 0.85*heating_load
elsif hpxml_bldg.header.heat_pump_backup_sizing_methodology == HPXML::HeatPumpBackupSizingSupplemental
if not hvac_heating.backup_heating_switchover_temp.nil?
min_compressor_temp = hvac_heating.backup_heating_switchover_temp
Expand Down