Skip to content

Commit

Permalink
Merge pull request #709 from NREL/ansi_301_2022_frac_duct_area
Browse files Browse the repository at this point in the history
ANSI 301-2022: Fraction Duct Area
  • Loading branch information
shorowit authored Jan 25, 2024
2 parents 7dfdb4c + bed481a commit 7ed2fbe
Show file tree
Hide file tree
Showing 10 changed files with 604 additions and 22 deletions.
6 changes: 3 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ PR Author: Check these when they're done. Not all may apply. ~~strikethrough~~ a
PR Reviewer: Verify each has been completed.

- [ ] OS-HPXML git subtree has been pulled
- [ ] 301/ES rulesets and unit tests have been updated
- [ ] 301validator.xml has been updated (reference EPvalidator.xml)
- [ ] Workflow tests have been updated
- [ ] Sample files have been added/updated (`openstudio tasks.rb update_hpxmls`)
- [ ] Tests have been added/updated (e.g., `rulesets\tests` and/or `workflow/tests/*_test.rb`)
- [ ] Documentation has been updated
- [ ] Changelog has been updated
- [ ] `openstudio tasks.rb update_measures` has been run
- [ ] No unexpected regression test changes on CI
- [ ] No unexpected changes to simulation results on CI
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ __New Features__
- Ground source heat pump model enhancements.
- Allows `Roof/RadiantBarrier` to be omitted; defaults to false.
- Adds more error-checking for inappropriate inputs (e.g., HVAC SHR=0 or clothes washer IMEF=0).
- Allows `FractionDuctArea` as alternative to `DuctSurfaceArea`

__Bugfixes__
- Fixes possible 301ruleset.rb error due to floating point arithmetic.
Expand Down
29 changes: 19 additions & 10 deletions docs/source/workflow_inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1532,16 +1532,16 @@ Additional information is entered in each ``DuctLeakageMeasurement``.

Additional information is entered in each ``Ducts``.

============================= ======= ============ =========== ======== ========== ===============================
Element Type Units Constraints Required Default Notes
============================= ======= ============ =========== ======== ========== ===============================
``SystemIdentifier`` id Yes Unique identifier
``DuctType`` string See [#]_ Yes Supply or return ducts
``DuctInsulationRValue`` double F-ft2-hr/Btu >= 0 Yes R-value of duct insulation [#]_
``DuctBuriedInsulationLevel`` string See [#]_ No not buried Duct buried insulation level [#]_
``DuctLocation`` string See [#]_ Yes Duct location
``DuctSurfaceArea`` double ft2 >= 0 Yes Duct surface area
============================= ======= ============ =========== ======== ========== ===============================
=============================================== ======= ============ ================ ======== ========== ======================================
Element Type Units Constraints Required Default Notes
=============================================== ======= ============ ================ ======== ========== ======================================
``SystemIdentifier`` id Yes Unique identifier
``DuctType`` string See [#]_ Yes Supply or return ducts
``DuctInsulationRValue`` double F-ft2-hr/Btu >= 0 Yes R-value of duct insulation [#]_
``DuctBuriedInsulationLevel`` string See [#]_ No not buried Duct buried insulation level [#]_
``DuctLocation`` string See [#]_ Yes Duct location
``FractionDuctArea`` and/or ``DuctSurfaceArea`` double frac or ft2 0-1 or >= 0 [#]_ Yes [#]_ See [#]_ Duct fraction/surface area in location
=============================================== ======= ============ ================ ======== ========== ======================================

.. [#] DuctType choices are "supply" or "return".
.. [#] DuctInsulationRValue should not include the exterior air film (i.e., use 0 for an uninsulated duct).
Expand All @@ -1554,6 +1554,15 @@ Additional information is entered in each ``Ducts``.
See the `Building America Solution Center <https://basc.pnnl.gov/resource-guides/ducts-buried-attic-insulation>`_ for more information.
.. [#] DuctLocation choices are "conditioned space", "basement - conditioned", "basement - unconditioned", "crawlspace - unvented", "crawlspace - vented", "attic - unvented", "attic - vented", "garage", "outside", "exterior wall", "under slab", "roof deck", "other housing unit", "other heated space", "other multifamily buffer space", or "other non-freezing space".
See :ref:`hpxmllocations` for descriptions.
.. [#] The sum of all FractionDuctArea must each equal to 1, both for the supply side and return side.
.. [#] If both are provided, DuctSurfaceArea will be used in the model.
.. [#] If DuctSurfaceArea not provided, duct surface areas will be calculated based on ANSI/RESNET/ICC 301-2022:
\- **Supply duct area**: 0.27 * ConditionedFloorAreaServed
\- **Return duct area**: (if NumberofReturnRegisters < 6, 0.05 * NumberofReturnRegisters, 0.25 otherwise) * ConditionedFloorAreaServed
where each duct surface area will be FractionDuctArea multiplied by supply, or return, duct area.
.. _hvac_distribution_hydronic:

Expand Down
1 change: 1 addition & 0 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,7 @@ def self.set_systems_hvac_rated(orig_bldg, new_bldg)
duct_insulation_r_value: orig_duct.duct_insulation_r_value,
duct_location: orig_duct.duct_location,
duct_surface_area: orig_duct.duct_surface_area,
duct_fraction_area: orig_duct.duct_fraction_area,
duct_buried_insulation_level: orig_duct.duct_buried_insulation_level)
end
end
Expand Down
5 changes: 4 additions & 1 deletion rulesets/resources/301validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1100,9 +1100,12 @@
<sch:assert role='ERROR' test='h:DuctBuriedInsulationLevel[text()="not buried" or text()="partially buried" or text()="fully buried" or text()="deeply buried"] or not(h:DuctBuriedInsulationLevel)'>Expected DuctBuriedInsulationLevel to be 'not buried' or 'partially buried' or 'fully buried' or 'deeply buried'</sch:assert>
<sch:assert role='ERROR' test='count(h:DuctLocation) = 1'>Expected 1 element(s) for xpath: DuctLocation</sch:assert> <!-- See [HVACDuct=WithLocation] or [HVACDuct=WithoutLocation] -->
<sch:assert role='ERROR' test='h:DuctLocation[text()="conditioned space" or text()="basement - conditioned" or text()="basement - unconditioned" or text()="crawlspace - vented" or text()="crawlspace - unvented" or text()="attic - vented" or text()="attic - unvented" or text()="garage" or text()="exterior wall" or text()="under slab" or text()="roof deck" or text()="outside" or text()="other housing unit" or text()="other heated space" or text()="other multifamily buffer space" or text()="other non-freezing space"] or not(h:DuctLocation)'>Expected DuctLocation to be 'conditioned space' or 'basement - conditioned' or 'basement - unconditioned' or 'crawlspace - vented' or 'crawlspace - unvented' or 'attic - vented' or 'attic - unvented' or 'garage' or 'exterior wall' or 'under slab' or 'roof deck' or 'outside' or 'other housing unit' or 'other heated space' or 'other multifamily buffer space' or 'other non-freezing space'</sch:assert>
<sch:assert role='ERROR' test='count(h:DuctSurfaceArea) = 1'>Expected 1 element(s) for xpath: DuctSurfaceArea</sch:assert>
<sch:assert role='ERROR' test='count(h:FractionDuctArea) + count(h:DuctSurfaceArea) &gt;= 1'>Expected 1 or more element(s) for xpath: FractionDuctArea | DuctSurfaceArea</sch:assert>
<sch:assert role='ERROR' test='count(../h:NumberofReturnRegisters) = 1'>Expected 1 element(s) for xpath: ../NumberofReturnRegisters</sch:assert>
<sch:assert role='ERROR' test='count(../../../h:ConditionedFloorAreaServed) = 1'>Expected 1 element(s) for xpath: ../../../ConditionedFloorAreaServed</sch:assert>
<!-- Sum Checks -->
<sch:assert role='ERROR' test='(sum(h:Ducts[h:DuctType="supply"]/h:FractionDuctArea) &gt;= 0.99 and sum(h:Ducts[h:DuctType="supply"]/h:FractionDuctArea) &lt;= 1.01) or count(h:Ducts[h:DuctType="supply"]/h:FractionDuctArea) = 0'>Expected sum(Ducts/FractionDuctArea) for DuctType="supply" to be 1</sch:assert>
<sch:assert role='ERROR' test='(sum(h:Ducts[h:DuctType="return"]/h:FractionDuctArea) &gt;= 0.99 and sum(h:Ducts[h:DuctType="return"]/h:FractionDuctArea) &lt;= 1.01) or count(h:Ducts[h:DuctType="return"]/h:FractionDuctArea) = 0'>Expected sum(Ducts/FractionDuctArea) for DuctType="return" to be 1</sch:assert>
</sch:rule>
</sch:pattern>

Expand Down
3 changes: 2 additions & 1 deletion rulesets/resources/ES_ZERHruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,8 @@ def self.get_radiant_barrier_bool(orig_bldg)

ducts_in_uncond_attic = false
all_ducts.each do |duct|
if [HPXML::LocationAtticVented, HPXML::LocationAtticUnvented].include?(duct.duct_location) && duct.duct_surface_area > 0
if [HPXML::LocationAtticVented, HPXML::LocationAtticUnvented].include?(duct.duct_location) &&
(!duct.duct_surface_area.to_f.zero? || !duct.duct_fraction_area.to_f.zero?)
ducts_in_uncond_attic = true
end
end
Expand Down
20 changes: 20 additions & 0 deletions rulesets/tests/test_hvac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,26 @@ def test_ducts_buried
end
end

def test_duct_fraction_area
hpxml_name = 'base-hvac-ducts-area-fractions.xml'
hpxml = HPXML.new(hpxml_path: File.join(@root_path, 'workflow', 'sample_files', hpxml_name))
hpxml_bldg = hpxml.buildings[0]
hpxml_name = File.basename(@tmp_hpxml_path)
XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path)

_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIRatedHome].include? calc_type
_check_ducts(hpxml_bldg, [{ duct_type: HPXML::DuctTypeSupply, duct_rvalue: 4.0, duct_area: 820.1, duct_location: HPXML::LocationAtticUnvented, duct_buried: HPXML::DuctBuriedInsulationNone },
{ duct_type: HPXML::DuctTypeReturn, duct_rvalue: 0.0, duct_area: 455.6, duct_location: HPXML::LocationAtticUnvented, duct_buried: HPXML::DuctBuriedInsulationNone },
{ duct_type: HPXML::DuctTypeSupply, duct_rvalue: 4.0, duct_area: 273.4, duct_location: HPXML::LocationExteriorWall, duct_buried: HPXML::DuctBuriedInsulationNone },
{ duct_type: HPXML::DuctTypeReturn, duct_rvalue: 0.0, duct_area: 151.9, duct_location: HPXML::LocationConditionedSpace, duct_buried: HPXML::DuctBuriedInsulationNone }])
else
_check_ducts(hpxml_bldg)
end
end
end

def test_dse
hpxml_name = 'base-hvac-dse.xml'

Expand Down
23 changes: 16 additions & 7 deletions tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,7 @@ def create_sample_hpxmls
'base-hvac-central-ac-only-var-speed.xml',
'base-hvac-central-ac-plus-air-to-air-heat-pump-heating.xml',
'base-hvac-dse.xml',
'base-hvac-ducts-area-fractions.xml',
'base-hvac-ducts-leakage-cfm50.xml',
'base-hvac-ducts-buried.xml',
'base-hvac-dual-fuel-air-to-air-heat-pump-1-speed.xml',
Expand Down Expand Up @@ -2947,7 +2948,7 @@ def create_sample_hpxmls
end
end

command_list = [:update_measures, :create_release_zips]
command_list = [:update_measures, :update_hpxmls, :create_release_zips]

def display_usage(command_list)
puts "Usage: openstudio #{File.basename(__FILE__)} [COMMAND]\nCommands:\n " + command_list.join("\n ")
Expand All @@ -2968,16 +2969,10 @@ def display_usage(command_list)
end

if ARGV[0].to_sym == :update_measures
require 'oga'
require_relative 'rulesets/resources/constants'

# Prevent NREL error regarding U: drive when not VPNed in
ENV['HOME'] = 'C:' if !ENV['HOME'].nil? && ENV['HOME'].start_with?('U:')
ENV['HOMEDRIVE'] = 'C:\\' if !ENV['HOMEDRIVE'].nil? && ENV['HOMEDRIVE'].start_with?('U:')

create_test_hpxmls
create_sample_hpxmls

# Apply rubocop
cops = ['Layout',
'Lint/DeprecatedClassMethods',
Expand Down Expand Up @@ -3013,6 +3008,20 @@ def display_usage(command_list)
puts 'Done.'
end

if ARGV[0].to_sym == :update_hpxmls
require 'oga'
require_relative 'rulesets/resources/constants'

# Prevent NREL error regarding U: drive when not VPNed in
ENV['HOME'] = 'C:' if !ENV['HOME'].nil? && ENV['HOME'].start_with?('U:')
ENV['HOMEDRIVE'] = 'C:\\' if !ENV['HOMEDRIVE'].nil? && ENV['HOMEDRIVE'].start_with?('U:')

t = Time.now
create_test_hpxmls
create_sample_hpxmls
puts "Completed in #{(Time.now - t).round(1)}s"
end

if ARGV[0].to_sym == :create_release_zips
require_relative 'workflow/version'

Expand Down
Loading

0 comments on commit 7ed2fbe

Please sign in to comment.