Skip to content

ACCA Manual S. Update #1852

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

Draft
wants to merge 14 commits into
base: resnet_heat_pump
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 @@ -346,7 +346,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
133 changes: 109 additions & 24 deletions HPXMLtoOpenStudio/resources/hvac_sizing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1712,27 +1712,109 @@ def self.apply_hvac_fractions_load_served(hvac_loads, hvac_heating, hvac_cooling
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)

# get_hvac_size_limits_cooling() 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 Man. S 2014, only cooling size limits are returned.
# Therefore, only sections/tables N2.3.1 and N2.3.2 from Man. S 2023 are addressed in this method.

return oversize_limit, oversize_delta, undersize_limit
# @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_cooling(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
# Man. S 2014 doesn't specify latent/sensible capacity undersize limits in this situation.
# could either return 1 (i.e. no adjustment) or nil for sens/latent undersize limit
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

def self.get_hvac_size_limits_heating(hvac_cooling, hvac_sizings, hpxml_bldg, weather, runner)
if hpxml_bldg.header.heat_pump_sizing_methodology == HPXML::HeatPumpSizingACCA
runner.registerError("No heating size limits ")

# Transfers the design load totals from the HVAC loads object to the HVAC sizings object.
#
# @param hvac_sizings [HVACSizingValues] Object with sizing values for a given HVAC system
Expand Down Expand Up @@ -2628,7 +2710,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
clg_ap = hvac_cooling.additional_properties
is_ducted = !hvac_cooling.distribution_system.nil?
cooling_delta_t = mj.cool_setpoint - clg_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 @@ -2707,17 +2789,17 @@ 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
hvac_sizings.Cool_Capacity_Sens = hvac_sizings.Cool_Capacity * clg_ap.cool_rated_shr_gross

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 @@ -2727,7 +2809,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 @@ -2799,7 +2881,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 * clg_ap.cool_rated_shr_gross
hvac_sizings.Cool_Airflow = calc_airflow_rate(:clg, hvac_cooling, hvac_sizings.Cool_Capacity, hpxml_bldg)
Expand Down Expand Up @@ -2859,6 +2941,7 @@ def self.apply_hvac_equipment_adjustments(mj, runner, hvac_sizings, weather, hva
HPXML::HVACTypeHeatPumpPTHP,
HPXML::HVACTypeHeatPumpRoom].include? heating_type

# TODO: refactor out oversize_delta
process_heat_pump_adjustment(mj, runner, hvac_sizings, weather, hvac_heating, total_cap_curve_value, hvac_system, oversize_limit, oversize_delta, hpxml_bldg)

hvac_sizings.Heat_Capacity_Supp = calculate_heat_pump_backup_load(mj, hvac_heating, hvac_sizings.Heat_Load_Supp, hvac_sizings.Heat_Capacity, hpxml_bldg)
Expand Down Expand Up @@ -3571,8 +3654,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, 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
Loading