Skip to content

Commit

Permalink
Merge pull request #717 from NREL/mf_aext_infiltration
Browse files Browse the repository at this point in the history
MF Aext infiltration
  • Loading branch information
shorowit authored Mar 6, 2024
2 parents 331bbe3 + d24ac0e commit 730f359
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 113 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ __New Features__
- **Breaking change**: Each `VentilationFan` must have one (and only one) `UsedFor...` element set to true.

__Bugfixes__
- Fixes incorrect Reference Home mechanical ventilation flowrate for attached units (when Aext is not 1).
- Fixes possible 301ruleset.rb error due to floating point arithmetic.

## OpenStudio-ERI v1.7.0
Expand Down
112 changes: 55 additions & 57 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def self.apply_reference_home_ruleset(orig_hpxml, iecc_version: nil, is_all_elec
set_enclosure_windows_reference(orig_bldg, new_bldg)
set_enclosure_skylights_reference(orig_bldg, new_bldg)
set_enclosure_doors_reference(orig_bldg, new_bldg)
set_enclosure_air_infiltration_reference(new_bldg)
set_enclosure_air_infiltration_reference(orig_bldg, new_bldg)

# Systems
set_systems_hvac_reference(orig_bldg, new_bldg, is_all_electric: is_all_electric)
Expand Down Expand Up @@ -276,7 +276,6 @@ def self.set_summary_reference(orig_bldg, new_bldg)
@nbeds = orig_bldg.building_construction.number_of_bedrooms
@ncfl = orig_bldg.building_construction.number_of_conditioned_floors
@ncfl_ag = orig_bldg.building_construction.number_of_conditioned_floors_above_grade
_sla, _ach50, _nach, @infil_volume, @infil_height = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)

new_bldg.site.fuels = orig_bldg.site.fuels
new_bldg.site.site_type = HPXML::SiteTypeSuburban
Expand All @@ -296,7 +295,6 @@ def self.set_summary_rated(orig_bldg, new_bldg)
@nbeds = orig_bldg.building_construction.number_of_bedrooms
@ncfl = orig_bldg.building_construction.number_of_conditioned_floors
@ncfl_ag = orig_bldg.building_construction.number_of_conditioned_floors_above_grade
_sla, _ach50, _nach, @infil_volume, @infil_height = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)

new_bldg.site.fuels = orig_bldg.site.fuels
new_bldg.site.site_type = HPXML::SiteTypeSuburban
Expand All @@ -316,7 +314,6 @@ def self.set_summary_iad(orig_bldg, new_bldg)
@nbeds = 3
@ncfl = 2.0
@ncfl_ag = 2.0
@infil_volume = 20400.0

new_bldg.site.fuels = orig_bldg.site.fuels
new_bldg.site.site_type = HPXML::SiteTypeSuburban
Expand All @@ -341,38 +338,38 @@ def self.set_climate(orig_bldg, new_bldg)
@is_southern_hemisphere = (@weather.header.Latitude < 0)
end

def self.set_enclosure_air_infiltration_reference(new_bldg)
@infil_a_ext = calc_mech_vent_Aext_ratio(new_bldg)
def self.set_enclosure_air_infiltration_reference(orig_bldg, new_bldg)
_, _, _, infil_volume, infil_height, _ = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)

sla = 0.00036
ach50 = Airflow.get_infiltration_ACH50_from_SLA(sla, 0.65, @cfa, @infil_volume)
ach50 = Airflow.get_infiltration_ACH50_from_SLA(sla, 0.65, @cfa, infil_volume)
new_bldg.air_infiltration_measurements.add(id: 'Infiltration_ACH50',
house_pressure: 50,
unit_of_measure: HPXML::UnitsACH,
air_leakage: ach50.round(2),
infiltration_volume: @infil_volume,
infiltration_height: @infil_height,
infiltration_volume: infil_volume,
infiltration_height: infil_height,
infiltration_type: HPXML::InfiltrationTypeUnitExterior,
a_ext: @infil_a_ext.round(3))
a_ext: 1.0)
end

def self.set_enclosure_air_infiltration_rated(orig_bldg, new_bldg)
@infil_a_ext = calc_mech_vent_Aext_ratio(new_bldg)
_, _, _, infil_volume, infil_height, _ = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)
ach50, a_ext = calc_rated_home_infiltration_ach50(orig_bldg)

ach50 = calc_rated_home_infiltration_ach50(orig_bldg)
new_bldg.air_infiltration_measurements.add(id: 'AirInfiltrationMeasurement',
house_pressure: 50,
unit_of_measure: HPXML::UnitsACH,
air_leakage: ach50.round(2),
infiltration_volume: @infil_volume,
infiltration_height: @infil_height,
infiltration_volume: infil_volume,
infiltration_height: infil_height,
infiltration_type: HPXML::InfiltrationTypeUnitExterior,
a_ext: @infil_a_ext.round(3))
a_ext: a_ext.round(3))
end

def self.set_enclosure_air_infiltration_iad(new_bldg)
@infil_height = new_bldg.inferred_infiltration_height(@infil_volume)
@infil_a_ext = calc_mech_vent_Aext_ratio(new_bldg)
infil_volume = 20400
infil_height = new_bldg.inferred_infiltration_height(infil_volume)

if ['1A', '1B', '1C', '2A', '2B', '2C'].include? @iecc_zone
ach50 = 5.0
Expand All @@ -383,10 +380,10 @@ def self.set_enclosure_air_infiltration_iad(new_bldg)
house_pressure: 50,
unit_of_measure: HPXML::UnitsACH,
air_leakage: ach50,
infiltration_volume: @infil_volume,
infiltration_height: @infil_height,
infiltration_volume: infil_volume,
infiltration_height: infil_height,
infiltration_type: HPXML::InfiltrationTypeUnitExterior,
a_ext: @infil_a_ext.round(3))
a_ext: 1.0)
end

def self.set_enclosure_attics_reference(orig_bldg, new_bldg)
Expand Down Expand Up @@ -1460,7 +1457,8 @@ def self.set_systems_mechanical_ventilation_reference(orig_bldg, new_bldg, iecc_
q_fan_airflow = (0.01 * @cfa) + (7.5 * (@nbeds + 1))
else
q_tot = Airflow.get_mech_vent_qtot_cfm(@nbeds, @cfa)
q_fan_airflow = calc_mech_vent_q_fan(q_tot, ref_sla, 0.0) # cfm for airflow
infil_height = new_bldg.air_infiltration_measurements[0].infiltration_height
q_fan_airflow = calc_mech_vent_qfan(q_tot, ref_sla, true, infil_height) # cfm for airflow
end

mech_vent_fans = orig_bldg.ventilation_fans.select { |f| f.used_for_whole_building_ventilation }
Expand Down Expand Up @@ -1627,15 +1625,9 @@ def self.set_systems_mechanical_ventilation_iad(new_bldg)
q_tot = Airflow.get_mech_vent_qtot_cfm(@nbeds, @cfa)

# Calculate fan cfm
sla = nil
new_bldg.air_infiltration_measurements.each do |new_infil_measurement|
next unless (new_infil_measurement.unit_of_measure == HPXML::UnitsACH) && (new_infil_measurement.house_pressure == 50)

ach50 = new_infil_measurement.air_leakage
sla = Airflow.get_infiltration_SLA_from_ACH50(ach50, 0.65, @cfa, @infil_volume)
break
end
q_fan = calc_mech_vent_q_fan(q_tot, sla, 0.0)
_, ach50, _, infil_volume, infil_height, _ = Airflow.get_values_from_air_infiltration_measurements(new_bldg, @cfa, @weather)
sla = Airflow.get_infiltration_SLA_from_ACH50(ach50, 0.65, @cfa, infil_volume)
q_fan = calc_mech_vent_qfan(q_tot, sla, true, infil_height)
fan_power_w = 0.70 * q_fan

new_bldg.ventilation_fans.add(id: 'MechanicalVentilation',
Expand Down Expand Up @@ -2443,18 +2435,31 @@ def self.calc_rated_home_q_fans_by_system(orig_bldg, all_mech_vent_fans)
end

def self.calc_rated_home_infiltration_ach50(orig_bldg)
_sla, ach50, _nach, _volume, _height = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)
# Note: The infiltration rate returned will *include* the Aext term.
# We separately report out the Aext in the HPXML file for inspection, but the
# AirInfiltrationMeasurement element will use infiltration_type: 'unit exterior'
# so as not to double-count the Aext term.
_, ach50, _, infil_volume, infil_height, _ = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)

if [HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include? @bldg_type
if (Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019'))
cfm50 = ach50 * @infil_volume / 60.0
tot_cb_area, _ext_cb_area = orig_bldg.compartmentalization_boundary_areas()
if cfm50 / tot_cb_area <= 0.30
ach50 *= @infil_a_ext
if @bldg_type == HPXML::ResidentialTypeSFD
a_ext = 1.0
else
tot_cb_area, ext_cb_area = orig_bldg.compartmentalization_boundary_areas()
a_ext = ext_cb_area / tot_cb_area

if Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019')
if [HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include? @bldg_type
cfm50 = ach50 * infil_volume / 60.0
tot_cb_area, _ext_cb_area = orig_bldg.compartmentalization_boundary_areas()
if cfm50 / tot_cb_area > 0.30
a_ext = 1.0
end
end
end
end

ach50 *= a_ext

# Apply min Natural ACH?
min_nach = nil
mech_vent_fans = orig_bldg.ventilation_fans.select { |f| f.used_for_whole_building_ventilation }
Expand All @@ -2463,21 +2468,21 @@ def self.calc_rated_home_infiltration_ach50(orig_bldg)
elsif Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019')
has_non_exhaust_systems = (mech_vent_fans.select { |f| f.fan_type != HPXML::MechVentTypeExhaust }.size > 0)
mech_vent_fans.each do |orig_vent_fan|
if orig_vent_fan.flow_rate_not_tested || ((@infil_a_ext < 0.5) && !has_non_exhaust_systems)
if orig_vent_fan.flow_rate_not_tested || ((a_ext < 0.5) && !has_non_exhaust_systems)
min_nach = 0.30
end
end
end

if not min_nach.nil?
min_sla = Airflow.get_infiltration_SLA_from_ACH(min_nach, @infil_height, @weather)
min_ach50 = Airflow.get_infiltration_ACH50_from_SLA(min_sla, 0.65, @cfa, @infil_volume)
min_sla = Airflow.get_infiltration_SLA_from_ACH(min_nach, infil_height, @weather)
min_ach50 = Airflow.get_infiltration_ACH50_from_SLA(min_sla, 0.65, @cfa, infil_volume)
if ach50 < min_ach50
ach50 = min_ach50
end
end

return ach50
return ach50, a_ext
end

def self.calc_mech_vent_supply_exhaust_cfms(ventilation_fans, total_or_oa)
Expand Down Expand Up @@ -2524,23 +2529,25 @@ def self.calc_mech_vent_is_balanced(ventilation_fans)
end

def self.calc_rated_home_qfan(orig_bldg, is_balanced)
ach50 = calc_rated_home_infiltration_ach50(orig_bldg)
sla = Airflow.get_infiltration_SLA_from_ACH50(ach50, 0.65, @cfa, @infil_volume)
_, _, _, infil_volume, infil_height, _ = Airflow.get_values_from_air_infiltration_measurements(orig_bldg, @cfa, @weather)
ach50, _ = calc_rated_home_infiltration_ach50(orig_bldg)
sla = Airflow.get_infiltration_SLA_from_ACH50(ach50, 0.65, @cfa, infil_volume)
q_tot = Airflow.get_mech_vent_qtot_cfm(@nbeds, @cfa)
q_fan_power = calc_mech_vent_q_fan(q_tot, sla, is_balanced)
return q_fan_power
q_fan = calc_mech_vent_qfan(q_tot, sla, is_balanced, infil_height)
return q_fan
end

def self.calc_mech_vent_q_fan(q_tot, sla, is_balanced)
nl = Airflow.get_infiltration_NL_from_SLA(sla, @infil_height)
def self.calc_mech_vent_qfan(q_tot, sla, is_balanced, infil_height)
nl = Airflow.get_infiltration_NL_from_SLA(sla, infil_height)
q_inf = nl * @weather.data.WSF * @cfa / 7.3 # Effective annual average infiltration rate, cfm, eq. 4.5a
a_ext = 1.0 # Use Aext=1.0 because sla reflects 'unit exterior' infiltration so it already incorporates Aext
if Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019')
if is_balanced
phi = 1.0
else
phi = q_inf / q_tot
end
q_fan = q_tot - phi * (q_inf * @infil_a_ext)
q_fan = q_tot - phi * (q_inf * a_ext)
else
if [HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include? @bldg_type
# No infiltration credit for attached/multifamily
Expand All @@ -2557,15 +2564,6 @@ def self.calc_mech_vent_q_fan(q_tot, sla, is_balanced)
return [q_fan, 0].max
end

def self.calc_mech_vent_Aext_ratio(hpxml)
tot_cb_area, ext_cb_area = hpxml.compartmentalization_boundary_areas()
if @bldg_type == HPXML::ResidentialTypeSFD
return 1.0
end

return ext_cb_area / tot_cb_area
end

def self.get_new_distribution_id(orig_bldg, new_bldg)
i = 0
while true
Expand Down
18 changes: 9 additions & 9 deletions rulesets/tests/test_ventilation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ def test_mech_vent_attached_or_multifamily
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIReferenceHome, Constants.CalcTypeCO2eReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 50.6, hours: 24, power: 0.0 }]) # Should have airflow but not fan energy
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 31.1, hours: 24, power: 0.0 }]) # Should have airflow but not fan energy
elsif [Constants.CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg)
elsif [Constants.CalcTypeERIIndexAdjustmentDesign].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 70.6, hours: 24, power: 49.4 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 60.0, hours: 24, power: 42.0 }])
elsif [Constants.CalcTypeERIIndexAdjustmentReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 32.1, hours: 24, power: 55.0 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 8.5, hours: 24, power: 49.4 }])
end
end

Expand Down Expand Up @@ -885,14 +885,14 @@ def test_mech_vent_shared
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIReferenceHome, Constants.CalcTypeCO2eReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 50.6, hours: 24, power: 19.7 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 31.1, hours: 24, power: 19.0 }])
elsif [Constants.CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeSupply, flowrate: 800.0, hours: 24, power: 240.0, in_unit_flowrate: 80.0, frac_recirc: 0.5, has_preheat: true, has_precool: true },
{ fantype: HPXML::MechVentTypeExhaust, flowrate: 72.0, hours: 24, power: 26.0 }])
elsif [Constants.CalcTypeERIIndexAdjustmentDesign].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 70.6, hours: 24, power: 49.4 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 60.0, hours: 24, power: 42.0 }])
elsif [Constants.CalcTypeERIIndexAdjustmentReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 32.1, hours: 24, power: 54.9 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 8.5, hours: 24, power: 49.4 }])
end
end
end
Expand All @@ -914,7 +914,7 @@ def test_mech_vent_shared_defaulted_fan_power
calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIReferenceHome, Constants.CalcTypeCO2eReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 50.6, hours: 24, power: 19.7 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 31.1, hours: 24, power: 19.0 }])
elsif [Constants.CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeSupply, flowrate: 800.0, hours: 24, power: 800.0, in_unit_flowrate: 80.0, frac_recirc: 0.5, has_preheat: true, has_precool: true },
{ fantype: HPXML::MechVentTypeExhaust, flowrate: 72.0, hours: 24, power: 26.0 }])
Expand All @@ -939,7 +939,7 @@ def test_mech_vent_shared_unmeasured_airflow_rate
calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIReferenceHome, Constants.CalcTypeCO2eReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 50.6, hours: 24, power: 17.9 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 31.1, hours: 24, power: 11.6 }])
elsif [Constants.CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeSupply, flowrate: 800.0, hours: 24, power: 240.0, in_unit_flowrate: 30.0, frac_recirc: 0.5, has_preheat: true, has_precool: true },
{ fantype: HPXML::MechVentTypeExhaust, flowrate: 72.0, hours: 24, power: 26.0 }])
Expand All @@ -966,7 +966,7 @@ def test_mech_vent_shared_unmeasured_airflow_rate_and_defaulted_fan_power
calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIReferenceHome, Constants.CalcTypeCO2eReferenceHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 50.6, hours: 24, power: 17.9 }])
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 31.1, hours: 24, power: 11.6 }])
elsif [Constants.CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeSupply, flowrate: 800.0, hours: 24, power: 800.0, in_unit_flowrate: 30.0, frac_recirc: 0.5, has_preheat: true, has_precool: true },
{ fantype: HPXML::MechVentTypeExhaust, flowrate: 72.0, hours: 24, power: 26.0 }])
Expand Down
18 changes: 9 additions & 9 deletions workflow/tests/base_results/EPA_Tests.csv
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[Version] XML,Reference Home ERI,Rated Home ERI
[MF_National_1.0] MFNCv1_CZ2_FL_gas_ground_corner_slab.xml,66.62,66.62
[MF_National_1.0] MFNCv1_CZ4_MO_gas_top_corner.xml,72.15,72.15
[MF_National_1.0] MFNCv1_CZ6_VT_elec_middle_interior.xml,67.71,67.71
[MF_National_1.1] MFNCv11_CZ2_FL_elec_top_corner.xml,61.71,61.71
[MF_National_1.1] MFNCv11_CZ4_MO_elec_ground_corner_vented_crawl.xml,61.97,61.97
[MF_National_1.1] MFNCv11_CZ6_VT_gas_ground_corner_cond_bsmt.xml,58.53,58.53
[MF_National_1.2] MFNCv12_CZ2_FL_gas_ground_corner_slab.xml,51.12,51.12
[MF_National_1.2] MFNCv12_CZ4_MO_gas_top_corner.xml,47.78,47.78
[MF_National_1.2] MFNCv12_CZ6_VT_elec_middle_interior.xml,55.72,55.72
[MF_National_1.0] MFNCv1_CZ2_FL_gas_ground_corner_slab.xml,66.99,66.99
[MF_National_1.0] MFNCv1_CZ4_MO_gas_top_corner.xml,74.15,74.15
[MF_National_1.0] MFNCv1_CZ6_VT_elec_middle_interior.xml,73.52,73.52
[MF_National_1.1] MFNCv11_CZ2_FL_elec_top_corner.xml,62.2,62.2
[MF_National_1.1] MFNCv11_CZ4_MO_elec_ground_corner_vented_crawl.xml,63.87,63.87
[MF_National_1.1] MFNCv11_CZ6_VT_gas_ground_corner_cond_bsmt.xml,62.19,62.19
[MF_National_1.2] MFNCv12_CZ2_FL_gas_ground_corner_slab.xml,51.4,51.4
[MF_National_1.2] MFNCv12_CZ4_MO_gas_top_corner.xml,49.03,49.03
[MF_National_1.2] MFNCv12_CZ6_VT_elec_middle_interior.xml,59.95,59.95
[SF_National_3.0] SFNHv3_CZ2_FL_gas_slab.xml,72.77,72.77
[SF_National_3.0] SFNHv3_CZ4_MO_gas_vented_crawl.xml,80.3,80.3
[SF_National_3.0] SFNHv3_CZ6_VT_elec_cond_bsmt.xml,70.0,70.0
Expand Down
Loading

0 comments on commit 730f359

Please sign in to comment.