Skip to content

Commit

Permalink
A CFIS system with no outdoor air control can still meet the DUMVS re…
Browse files Browse the repository at this point in the history
…quirement. Also, adds 2022CE as an ERI version.
  • Loading branch information
shorowit committed Oct 23, 2024
1 parent 2a5cc67 commit a56a4ee
Show file tree
Hide file tree
Showing 9 changed files with 1,048 additions and 59 deletions.
15 changes: 7 additions & 8 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

__New Features__
- Updates to HPXML v4.0 final release.
- Updates HERS Diagnostic Output to v0.3.0.
- Adds 2024 IECC ERI pathway calculation.
- **Breaking change**: Renamed `Emissions: <EmissionsType>: RESNET: XXX` to `Emissions: <EmissionsType>: ANSI301: XXX` in Annual Home CSV output files.
- Adds inputs for modeling skylight curbs and/or shafts.
- Implements ANSI/RESNET/ICC Standard 301-2022 Addendum E for CFIS systems.
- **Breaking change**: Removes `FanPower`/`FanPowerDefaulted` inputs for CFIS systems (fan power assumed to be HVAC's FanPowerWattsPerCFM multiplied by the blower fan max flow rate).
- **Breaking change**: Removes `VentilationOnlyModeAirflowFraction` input for CFIS systems (blower fan assumed to run at max flow rate during ventilation only operation).
- **Breaking change**: Adds `CFISControls/HasOutdoorAirControl` input for CFIS systems to describe whether there is automatic flow control of outdoor air.
- **Breaking change**: Adds `CFISControls/extension/ControlType` input ("optimized" or "timer") to describe how the CFIS air handler fan is controlled.
- Adds choice of "none" for `CFISControls/AdditionalRuntimeOperatingMode` input for CFIS systems to describe when there is no strategy to meet remainder of ventilation target.
- Allow modeling CFIS systems with supplemental fans that run simultaneously with the air handler (`CFISControls/extension/SupplementalFanRunsWithAirHandlerFan=true`).
- `ERICalculation/Version` and `CO2IndexCalculation/Version` can now be "2022CE".
- **Breaking change**: Removes `FanPower`/`FanPowerDefaulted` and `VentilationOnlyModeAirflowFraction` inputs for CFIS systems.
- **Breaking change**: Adds `CFISControls/HasOutdoorAirControl` and `CFISControls/extension/ControlType` inputs for CFIS systems.
- Adds choice of "none" for `CFISControls/AdditionalRuntimeOperatingMode` input for CFIS systems.
- Adds optional `CFISControls/extension/SupplementalFanRunsWithAirHandlerFan` input for CFIS systems.
- Adds inputs for modeling skylight curbs and/or shafts.
- Relaxes IECC climate zone requirements.
- IECC climate zone years other than 2006 are now always optional; for programs that use specific IECC climate zone years (e.g., 2021 for ZERH SF 2.0), that year is used if provided, otherwise the next earliest provided year will be used with the assumption that the climate zone has not changed across the years.
- See [the documentation](https://openstudio-eri.readthedocs.io/en/latest/workflow_inputs.html#hpxml-climate-zones) for more information.
- Updates HERS Diagnostic Output to v0.3.0.

__Bugfixes__
- Adds error-checking for `NumberofConditionedFloorsAboveGrade`=0, which is not allowed per the documentation.
Expand Down
4 changes: 2 additions & 2 deletions docs/source/workflow_inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ The OpenStudio-ERI calculation(s) to be performed are entered in ``/HPXML/Softwa
``ZERHCalculation/Version`` string See [#]_ No <none> Version to perform DOE ZERH ERI calculation
================================= ======== ======= =========== ======== ======= ==================================

.. [#] ERICalculation/Version choices are "latest", "2022C", "2022", "2019ABCD", "2019ABC", "2019AB", "2019A", "2019", "2014AEG", "2014AE", "2014A", or "2014".
.. [#] ERICalculation/Version choices are "latest", "2022CE", "2022C", "2022", "2019ABCD", "2019ABC", "2019AB", "2019A", "2019", "2014AEG", "2014AE", "2014A", or "2014".
For example, a value of "2019AB" tells the workflow to use ANSI/RESNET/ICC 301-2019 with both Addendum A and Addendum B included.
A value of "latest" can be used to always point to the latest version available.
.. [#] CO2IndexCalculation/Version choices are "latest", "2022C", "2022", or "2019ABCD".
.. [#] CO2IndexCalculation/Version choices are "latest", "2022CE", "2022C", "2022", or "2019ABCD".
A value of "latest" can be used to always point to the latest version available.
If both CO2IndexCalculation/Version and ERICalculation/Version are provided, they must use the same version.
.. [#] IECCERICalculation/Version choices are "2024", "2021", "2018", or "2015".
Expand Down
2 changes: 1 addition & 1 deletion hpxml-measures/HPXMLtoOpenStudio/resources/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ module Constants

# Arrays/Maps
ERIVersions = ['2014', '2014A', '2014AE', '2014AEG', '2019', '2019A',
'2019AB', '2019ABC', '2019ABCD', '2022', '2022C']
'2019AB', '2019ABC', '2019ABCD', '2022', '2022C', '2022CE']
IECCZones = ['1A', '1B', '1C', '2A', '2B', '2C', '3A', '3B', '3C',
'4A', '4B', '4C', '5A', '5B', '5C', '6A', '6B', '6C', '7', '8']
StateCodesMap = { 'AK' => 'Alaska',
Expand Down
19 changes: 11 additions & 8 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2522,13 +2522,16 @@ def self.calc_rated_home_infiltration_ach50(orig_bldg)
mech_vent_fans = orig_bldg.ventilation_fans.select { |f| f.used_for_whole_building_ventilation }
if mech_vent_fans.empty?
min_nach = 0.30
elsif mech_vent_fans.select { |f| f.fan_type == HPXML::MechVentTypeCFIS && (f.cfis_addtl_runtime_operating_mode == HPXML::CFISModeNone || !f.cfis_has_outdoor_air_control) }.size > 0
# 301-2022 Addendum E
# Does not quality as Dwelling Unit Mechanical Ventilation System because it has no
# strategy to meet remainder of ventilation target
min_nach = 0.30
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)
end
if Constants::ERIVersions.index(@eri_version) >= Constants::ERIVersions.index('2022CE')
if mech_vent_fans.any? { |f| f.fan_type == HPXML::MechVentTypeCFIS && f.cfis_addtl_runtime_operating_mode == HPXML::CFISModeNone }
# Does not quality as Dwelling Unit Mechanical Ventilation System because it has no
# strategy to meet remainder of ventilation target
min_nach = 0.30
end
end
if Constants::ERIVersions.index(@eri_version) >= Constants::ERIVersions.index('2019')
has_non_exhaust_systems = mech_vent_fans.any? { |f| f.fan_type != HPXML::MechVentTypeExhaust }
mech_vent_fans.each do |orig_vent_fan|
if orig_vent_fan.flow_rate_not_tested || ((a_ext < 0.5) && !has_non_exhaust_systems)
min_nach = 0.30
Expand Down Expand Up @@ -2790,7 +2793,7 @@ def self.add_reference_air_conditioner(orig_bldg, new_bldg, load_frac, orig_syst
def self.add_reference_distribution_system(new_bldg)
new_bldg.hvac_systems.each do |hvac|
next if hvac.distribution_system_idref.nil?
next if new_bldg.hvac_distributions.select { |d| d.id == hvac.distribution_system_idref }.size > 0
next if new_bldg.hvac_distributions.any? { |d| d.id == hvac.distribution_system_idref }

# Add new DSE distribution if distribution doesn't already exist
new_bldg.hvac_distributions.add(id: hvac.distribution_system_idref,
Expand Down
4 changes: 2 additions & 2 deletions rulesets/resources/301validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<sch:title>[Root]</sch:title>
<sch:rule context='/h:HPXML'>
<sch:assert role='ERROR' test='count(h:SoftwareInfo/h:extension/h:ERICalculation/h:Version) &lt;= 1'>Expected 0 or 1 element(s) for xpath: SoftwareInfo/extension/ERICalculation/Version</sch:assert> <!-- Required if running ERI calculation -->
<sch:assert role='ERROR' test='h:SoftwareInfo/h:extension/h:ERICalculation/h:Version[text()="latest" or text()="2022C" or text()="2022" or text()="2019ABCD" or text()="2019ABC" or text()="2019AB" or text()="2019A" or text()="2019" or text()="2014AEG" or text()="2014AE" or text()="2014A" or text()="2014"] or not(h:SoftwareInfo/h:extension/h:ERICalculation/h:Version)'>Expected SoftwareInfo/extension/ERICalculation/Version to be 'latest' or '2022C' or '2022' or '2019ABCD' or '2019ABC' or '2019AB' or '2019A' or '2019' or '2014AEG' or '2014AE' or '2014A' or '2014'</sch:assert>
<sch:assert role='ERROR' test='h:SoftwareInfo/h:extension/h:ERICalculation/h:Version[text()="latest" or text()="2022CE" or text()="2022C" or text()="2022" or text()="2019ABCD" or text()="2019ABC" or text()="2019AB" or text()="2019A" or text()="2019" or text()="2014AEG" or text()="2014AE" or text()="2014A" or text()="2014"] or not(h:SoftwareInfo/h:extension/h:ERICalculation/h:Version)'>Expected SoftwareInfo/extension/ERICalculation/Version to be 'latest' or '2022CE' or '2022C' or '2022' or '2019ABCD' or '2019ABC' or '2019AB' or '2019A' or '2019' or '2014AEG' or '2014AE' or '2014A' or '2014'</sch:assert>
<sch:assert role='ERROR' test='count(h:SoftwareInfo/h:extension/h:CO2IndexCalculation/h:Version) &lt;= 1'>Expected 0 or 1 element(s) for xpath: SoftwareInfo/extension/CO2IndexCalculation/Version</sch:assert> <!-- Required if running CO2 Index calculation -->
<sch:assert role='ERROR' test='h:SoftwareInfo/h:extension/h:CO2IndexCalculation/h:Version[text()="latest" or text()="2022C" or text()="2022" or text()="2019ABCD"] or not(h:SoftwareInfo/h:extension/h:CO2IndexCalculation/h:Version)'>Expected SoftwareInfo/extension/CO2IndexCalculation/Version to be 'latest' or '2022C' or '2022' or '2019ABCD'</sch:assert>
<sch:assert role='ERROR' test='h:SoftwareInfo/h:extension/h:CO2IndexCalculation/h:Version[text()="latest" or text()="2022CE" or text()="2022C" or text()="2022" or text()="2019ABCD"] or not(h:SoftwareInfo/h:extension/h:CO2IndexCalculation/h:Version)'>Expected SoftwareInfo/extension/CO2IndexCalculation/Version to be 'latest' or '2022CE' or '2022C' or '2022' or '2019ABCD'</sch:assert>
<sch:assert role='ERROR' test='count(h:SoftwareInfo/h:extension/h:IECCERICalculation/h:Version) &lt;= 1'>Expected 0 or 1 element(s) for xpath: SoftwareInfo/extension/IECCERICalculation/Version</sch:assert> <!-- Required if running IECC ERI pathway calculation -->
<sch:assert role='ERROR' test='h:SoftwareInfo/h:extension/h:IECCERICalculation/h:Version[text()="2024" or text()="2021" or text()="2018" or text()="2015"] or not(h:SoftwareInfo/h:extension/h:IECCERICalculation/h:Version)'>Expected SoftwareInfo/extension/IECCERICalculation/Version to be '2024' or '2021' or '2018' or '2015'</sch:assert>
<sch:assert role='ERROR' test='count(h:SoftwareInfo/h:extension/h:EnergyStarCalculation/h:Version) &lt;= 1'>Expected 0 or 1 element(s) for xpath: SoftwareInfo/extension/EnergyStarCalculation/Version</sch:assert> <!-- Required if running ENERGY STAR calculation -->
Expand Down
42 changes: 28 additions & 14 deletions rulesets/tests/test_enclosure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,34 @@ def test_enclosure_infiltration

# Test CFIS mechanical ventilation that does not quality as a
# Dwelling Unit Mechanical Ventilation System
hpxml_names = ['base-mechvent-cfis-no-additional-runtime.xml',
'base-mechvent-cfis-no-outdoor-air-control.xml']
hpxml_name = 'base-mechvent-cfis-no-additional-runtime.xml'

hpxml_names.each do |hpxml_name|
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIRatedHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 9.3, height: 9.75, volume: 21600.0) # 0.3 nACH
elsif [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 7.09, height: 9.75, volume: 21600.0)
elsif [Constants::CalcTypeERIIndexAdjustmentDesign].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 3.0, height: 17.0, volume: 20400.0)
elsif [Constants::CalcTypeERIIndexAdjustmentReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 6.67, height: 17.0, volume: 20400.0)
end
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIRatedHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 9.3, height: 9.75, volume: 21600.0) # 0.3 nACH
elsif [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 7.09, height: 9.75, volume: 21600.0)
elsif [Constants::CalcTypeERIIndexAdjustmentDesign].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 3.0, height: 17.0, volume: 20400.0)
elsif [Constants::CalcTypeERIIndexAdjustmentReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 6.67, height: 17.0, volume: 20400.0)
end
end

# Same as above but prior to 301-2022 Addendum E
hpxml_name = _change_eri_version(hpxml_name, '2022C')

_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIRatedHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 3.0, height: 9.75, volume: 21600.0) # not 0.3 nACH
elsif [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 7.09, height: 9.75, volume: 21600.0)
elsif [Constants::CalcTypeERIIndexAdjustmentDesign].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 3.0, height: 17.0, volume: 20400.0)
elsif [Constants::CalcTypeERIIndexAdjustmentReferenceHome].include? calc_type
_check_infiltration(hpxml_bldg, ach50: 6.67, height: 17.0, volume: 20400.0)
end
end
end
Expand Down Expand Up @@ -352,6 +365,7 @@ def test_enclosure_walls
_check_walls(hpxml_bldg, area: 2355.52, rvalue: 16.67, sabs: 0.75, emit: 0.9)
end
end

hpxml_name = _change_eri_version(hpxml_name, '2022')
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
Expand Down
33 changes: 9 additions & 24 deletions rulesets/tests/test_ventilation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -612,31 +612,28 @@ def test_mech_vent_cfis
hpxml_names.each do |hpxml_name|
cfis_suppl_flowrate = nil
cfis_suppl_power = nil
cfis_is_dumvs = true
cfis_suppl_fan_sync = nil
cfis_control_type = HPXML::CFISControlTypeOptimized
if hpxml_name == 'base-mechvent-cfis.xml'
if ['base-mechvent-cfis.xml',
'base-mechvent-cfis-no-outdoor-air-control.xml'].include? hpxml_name
cfis_mode = HPXML::CFISModeAirHandler
elsif hpxml_name == 'base-mechvent-cfis-no-outdoor-air-control.xml'
cfis_mode = HPXML::CFISModeAirHandler
cfis_is_dumvs = false
elsif hpxml_name == 'base-mechvent-cfis-supplemental-fan-exhaust.xml' || hpxml_name == 'base-mechvent-cfis-supplemental-fan-exhaust-synchronized.xml'
elsif ['base-mechvent-cfis-supplemental-fan-exhaust.xml',
'base-mechvent-cfis-supplemental-fan-exhaust-synchronized.xml'].include? hpxml_name
cfis_mode = HPXML::CFISModeSupplementalFan
cfis_suppl_flowrate = 120.0
cfis_suppl_power = 30.0
cfis_suppl_fan_sync = (hpxml_name == 'base-mechvent-cfis-supplemental-fan-exhaust-synchronized.xml')
elsif hpxml_name == 'base-mechvent-cfis-no-additional-runtime.xml'
elsif ['base-mechvent-cfis-no-additional-runtime.xml'].include? hpxml_name
cfis_mode = HPXML::CFISModeNone
cfis_is_dumvs = false
elsif hpxml_name == 'base-mechvent-cfis-control-type-timer.xml'
elsif ['base-mechvent-cfis-control-type-timer.xml'].include? hpxml_name
cfis_mode = HPXML::CFISModeAirHandler
cfis_control_type = HPXML::CFISControlTypeTimer
end

_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
if not cfis_is_dumvs
if hpxml_name == 'base-mechvent-cfis-no-additional-runtime.xml'
# CFIS doesn't qualify as a Dwelling Unit Mechanical Ventilation System, so rated home gets 0.3 nACH and
# ventilation requirement is lower, resulting in lower Reference Home fan power
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 27.0, hours: 24, power: 2.1 }])
Expand All @@ -659,13 +656,7 @@ def test_mech_vent_cfis
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
if not cfis_is_dumvs
# CFIS doesn't qualify as a Dwelling Unit Mechanical Ventilation System, so rated home gets 0.3 nACH and
# ventilation requirement is lower, resulting in lower Reference Home fan power
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 27.0, hours: 24, power: 0.2 }])
else
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 27.0, hours: 24, power: 34.9 }])
end
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 27.0, hours: 24, power: 34.9 }])
elsif [Constants::CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeCFIS, flowrate: 330.0, hours: 8, cfis_mode: cfis_mode,
cfis_suppl_flowrate: cfis_suppl_flowrate, cfis_suppl_power: cfis_suppl_power,
Expand All @@ -682,13 +673,7 @@ def test_mech_vent_cfis
_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants::CalcTypeERIReferenceHome, Constants::CalcTypeCO2eReferenceHome].include? calc_type
if not cfis_is_dumvs
# CFIS doesn't qualify as a Dwelling Unit Mechanical Ventilation System, so rated home gets 0.3 nACH and
# ventilation requirement is lower, resulting in lower Reference Home fan power
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 37.0, hours: 24, power: 13.0 }])
else
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 37.0, hours: 24, power: 26.4 }])
end
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeBalanced, flowrate: 37.0, hours: 24, power: 26.4 }])
elsif [Constants::CalcTypeERIRatedHome].include? calc_type
_check_mech_vent(hpxml_bldg, [{ fantype: HPXML::MechVentTypeCFIS, flowrate: 330.0, hours: 8, cfis_mode: cfis_mode,
cfis_suppl_flowrate: cfis_suppl_flowrate, cfis_suppl_power: cfis_suppl_power,
Expand Down
Loading

0 comments on commit a56a4ee

Please sign in to comment.