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

ANSI 301-2022: Fraction Duct Area #709

Merged
merged 9 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
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
39 changes: 29 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 [#]_ See [#]_ 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,25 @@ 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.
bpark1327 marked this conversation as resolved.
Show resolved Hide resolved
.. [#] FractionDuctArea or DuctSurfaceArea are required if DuctLocation is provided. If both are provided, DuctSurfaceArea will be used in the model.
bpark1327 marked this conversation as resolved.
Show resolved Hide resolved
.. [#] If neither DuctSurfaceArea nor FractionDuctArea provided, duct surface areas will be calculated based on `ASHRAE Standard 152 <https://www.energy.gov/eere/buildings/downloads/ashrae-standard-152-spreadsheet>`_:
bpark1327 marked this conversation as resolved.
Show resolved Hide resolved

\- **Primary supply duct area**: 0.27 * F_out * ConditionedFloorAreaServed

\- **Secondary supply duct area**: 0.27 * (1 - F_out) * ConditionedFloorAreaServed

\- **Total supply duct area**: **Primary supply duct area** + **Secondary supply duct area**

\- **Primary return duct area**: b_r * F_out * ConditionedFloorAreaServed

\- **Secondary return duct area**: b_r * (1 - F_out) * ConditionedFloorAreaServed

\- **Total return duct area**: **Primary return duct area** + **Secondary return duct area**

where F_out is 1.0 when NumberofConditionedFloorsAboveGrade <= 1 and 0.75 when NumberofConditionedFloorsAboveGrade > 1, and b_r is 0.05 * NumberofReturnRegisters with a maximum value of 0.25.

If FractionDuctArea is provided, each duct surface area will be FractionDuctArea times total duct area, which is calculated using the sum of primary and secondary duct areas from the equations above.
bpark1327 marked this conversation as resolved.
Show resolved Hide resolved

.. _hvac_distribution_hydronic:

Expand Down
15 changes: 13 additions & 2 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1415,12 +1415,23 @@ def self.set_systems_hvac_rated(orig_bldg, new_bldg)

# Ducts
orig_hvac_distribution.ducts.each do |orig_duct|
if orig_duct.duct_surface_area.nil?
# Default duct surface area(s)
cfa_served = orig_hvac_distribution.conditioned_floor_area_served
n_returns = orig_hvac_distribution.number_of_return_registers
total_duct_area = HVAC.get_default_duct_surface_area(orig_duct.duct_type, @ncfl_ag, cfa_served, n_returns).sum()
duct_surface_area = total_duct_area * orig_duct.duct_fraction_area
duct_surface_area_isdefaulted = true
else
duct_surface_area = orig_duct.duct_surface_area
end
new_hvac_distribution.ducts.add(id: orig_duct.id,
duct_type: orig_duct.duct_type,
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_buried_insulation_level: orig_duct.duct_buried_insulation_level)
duct_surface_area: duct_surface_area,
duct_buried_insulation_level: orig_duct.duct_buried_insulation_level,
duct_surface_area_isdefaulted: duct_surface_area_isdefaulted)
bpark1327 marked this conversation as resolved.
Show resolved Hide resolved
end
end

Expand Down
2 changes: 1 addition & 1 deletion rulesets/resources/301validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,7 @@
<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>
</sch:rule>
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
24 changes: 24 additions & 0 deletions rulesets/tests/test_hvac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,30 @@ def test_ducts_buried
end
end

def test_duct_fraction_area
hpxml_name = 'base.xml'
hpxml = HPXML.new(hpxml_path: File.join(@root_path, 'workflow', 'sample_files', hpxml_name))
hpxml_bldg = hpxml.buildings[0]
hpxml_bldg.hvac_distributions.each do |hvac_distribution|
hvac_distribution.ducts.each do |duct|
duct.duct_surface_area = nil
duct.duct_fraction_area = 0.7
end
end
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: 510.3, duct_location: HPXML::LocationAtticUnvented, duct_buried: HPXML::DuctBuriedInsulationNone },
{ duct_type: HPXML::DuctTypeReturn, duct_rvalue: 0.0, duct_area: 189.0, duct_location: HPXML::LocationAtticUnvented, duct_buried: HPXML::DuctBuriedInsulationNone }])
else
_check_ducts(hpxml_bldg)
end
end
end

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

Expand Down