Skip to content

Commit

Permalink
Squashed 'hpxml-measures/' changes from b651b6900d..f88480c299
Browse files Browse the repository at this point in the history
f88480c299 Latest results.
2e37ac4454 Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
b10fd27d1e Latest results.
1a02a94d30 Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
6050925349 Latest results.
5ff6774afa Merge branch 'master' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
45cdd444fd Latest results.
6d6d0177e0 Merge branch 'ansi_301_2022' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
dc687d155e Merge branch 'ansi_301_2022' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
6be89690f6 Expose WithinInfiltrationVolume inputs for some attic/foundation types.
d42c040f55 Minor refactoring.
46fe162b76 Merge branch 'ansi_301_2022_airflow' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
1092b38dee Handle case where Qinf > Qtot.
8db9c4dd9c Latest results.
fbf067fdcb Fix assert_in_epsilon tests.
c3c95819d2 Simplify code.
9dabadd9b4 Update Changelog.md [ci skip]
1115e941ee Fix typo in docs.
b704b1968c Merge branch 'ansi_301_2022' of https://github.com/NREL/OpenStudio-HPXML into ansi_301_2022_airflow
d60d17e34b Updates equations for Qfan and hourly combining of infiltration/ventilation.

git-subtree-dir: hpxml-measures
git-subtree-split: f88480c29951a2f9bd4fe705fd92509a45cb6781
  • Loading branch information
shorowit committed Mar 6, 2024
1 parent 21f83af commit aaf1a63
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 193 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ __New Features__
- Adds schedule inputs for hot water recirculation pumps and general water use internal gains.
- Updated water heater installation location defaulting to match ANSI 301-2022
- Updated calculation of hot water piping length for buildings with both conditioned and unconditioned basements to avoid double counting.
- Updates how imbalanced infiltration and mechanical ventilation are combined on an hourly basis.
- Small change to default flow rate for imbalanced mechanical ventilation systems.
- `AverageCeilingHeight` now used in natural ACH/CFM infiltration calculations.
- **Breaking change**: Replaces `BuildingSummary/Site/extension/GroundConductivity` with `BuildingSummary/Site/Soil/Conductivity`.
- **Breaking change**: Modeling whole SFA/MF buildings is now specified using a `SoftwareInfo/extension/WholeSFAorMFBuildingSimulation=true` element instead of `building-id=ALL` argument.
Expand Down
16 changes: 8 additions & 8 deletions HPXMLtoOpenStudio/measure.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<schema_version>3.1</schema_version>
<name>hpxm_lto_openstudio</name>
<uid>b1543b30-9465-45ff-ba04-1d1f85e763bc</uid>
<version_id>abc7b14e-b976-4545-85fa-5dc38c652087</version_id>
<version_modified>2024-03-05T21:31:00Z</version_modified>
<version_id>73a60fd3-fafd-41d8-a5eb-f95cb12f6cfe</version_id>
<version_modified>2024-03-06T01:32:05Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -148,7 +148,7 @@
<filename>airflow.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>57F45E12</checksum>
<checksum>FEDFA435</checksum>
</file>
<file>
<filename>battery.rb</filename>
Expand Down Expand Up @@ -304,13 +304,13 @@
<filename>hpxml.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>8CC9070B</checksum>
<checksum>758D1948</checksum>
</file>
<file>
<filename>hpxml_defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>6EF6A922</checksum>
<checksum>F5FE95AC</checksum>
</file>
<file>
<filename>hpxml_schema/HPXML.xsd</filename>
Expand All @@ -328,7 +328,7 @@
<filename>hpxml_schematron/EPvalidator.xml</filename>
<filetype>xml</filetype>
<usage_type>resource</usage_type>
<checksum>DE09AAEE</checksum>
<checksum>1407BC14</checksum>
</file>
<file>
<filename>hpxml_schematron/iso-schematron.xsd</filename>
Expand All @@ -346,7 +346,7 @@
<filename>hvac_sizing.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>6D041214</checksum>
<checksum>903730A2</checksum>
</file>
<file>
<filename>lighting.rb</filename>
Expand Down Expand Up @@ -598,7 +598,7 @@
<filename>test_defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>test</usage_type>
<checksum>19A04DB8</checksum>
<checksum>C0F759DF</checksum>
</file>
<file>
<filename>test_enclosure.rb</filename>
Expand Down
91 changes: 65 additions & 26 deletions HPXMLtoOpenStudio/resources/airflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,22 @@ def self.apply(model, runner, weather, spaces, hpxml_header, hpxml_bldg, cfa, nb
vented_attic = hpxml_bldg.attics.find { |attic| attic.attic_type == HPXML::AtticTypeVented }
vented_crawl = hpxml_bldg.foundations.find { |foundation| foundation.foundation_type == HPXML::FoundationTypeCrawlspaceVented }

_, conditioned_ach50, nach, infil_volume, infil_height, a_ext = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
infil_values = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
if @apply_ashrae140_assumptions
conditioned_const_ach = nach
conditioned_const_ach = infil_values[:nach]
conditioned_ach50 = nil
else
conditioned_ach50 = infil_values[:ach50]
conditioned_const_ach = nil
end
conditioned_const_ach *= a_ext unless conditioned_const_ach.nil?
conditioned_ach50 *= a_ext unless conditioned_ach50.nil?
conditioned_const_ach *= infil_values[:a_ext] unless conditioned_const_ach.nil?
conditioned_ach50 *= infil_values[:a_ext] unless conditioned_ach50.nil?
has_flue_chimney_in_cond_space = hpxml_bldg.air_infiltration.has_flue_or_chimney_in_conditioned_space

apply_natural_ventilation_and_whole_house_fan(model, hpxml_bldg.site, vent_fans_whf, open_window_area, clg_ssn_sensor, hpxml_bldg.header.natvent_days_per_week,
infil_volume, infil_height, unavailable_periods)
infil_values[:volume], infil_values[:height], unavailable_periods)
apply_infiltration_and_ventilation_fans(model, weather, hpxml_bldg.site, vent_fans_mech, vent_fans_kitchen, vent_fans_bath, vented_dryers,
has_flue_chimney_in_cond_space, conditioned_ach50, conditioned_const_ach, infil_volume, infil_height,
has_flue_chimney_in_cond_space, conditioned_ach50, conditioned_const_ach, infil_values[:volume], infil_values[:height],
vented_attic, vented_crawl, clg_ssn_sensor, schedules_file, vent_fans_cfis_suppl, unavailable_periods, hpxml_bldg.elevation)
end

Expand Down Expand Up @@ -215,31 +218,23 @@ def self.get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
end
a_ext = 1.0 if a_ext.nil?

return sla, ach50, nach, infil_volume, infil_height, a_ext
return { sla: sla, ach50: ach50, nach: nach, volume: infil_volume, height: infil_height, a_ext: a_ext }
end

def self.get_default_mech_vent_flow_rate(hpxml_bldg, vent_fan, weather, cfa, nbeds)
# Calculates Qfan cfm requirement per ASHRAE 62.2-2019
sla, _, _, _, infil_height, a_ext = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
def self.get_default_mech_vent_flow_rate(hpxml_bldg, vent_fan, weather, cfa, nbeds, eri_version)
# Calculates Qfan cfm requirement per ASHRAE 62.2 / ANSI 301
infil_values = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
bldg_type = hpxml_bldg.building_construction.residential_facility_type

nl = get_infiltration_NL_from_SLA(sla, infil_height)
nl = get_infiltration_NL_from_SLA(infil_values[:sla], infil_values[:height])
q_inf = get_infiltration_Qinf_from_NL(nl, weather, cfa)

q_tot = get_mech_vent_qtot_cfm(nbeds, cfa)

if vent_fan.is_balanced?
phi = 1.0
is_balanced, frac_imbal = true, 0.0
else
phi = q_inf / q_tot
is_balanced, frac_imbal = false, 1.0
end
q_fan = q_tot - phi * (q_inf * a_ext)
q_fan = [q_fan, 0].max

if not vent_fan.hours_in_operation.nil?
# Convert from hourly average requirement to actual fan flow rate
q_fan *= 24.0 / vent_fan.hours_in_operation
end

q_fan = get_mech_vent_qfan_cfm(q_tot, q_inf, is_balanced, frac_imbal, infil_values[:a_ext], bldg_type, eri_version, vent_fan.hours_in_operation)
return q_fan
end

Expand Down Expand Up @@ -1634,7 +1629,10 @@ def self.apply_infiltration_adjustment_to_conditioned(model, infil_program, vent
infil_program.addLine('Set Qexhaust = Qrange + Qbath + Qdryer + QWHV_exh + QWHV_cfis_suppl_exh')
infil_program.addLine('Set Qsupply = QWHV_sup + QWHV_cfis_sup + QWHV_cfis_suppl_sup')
infil_program.addLine('Set Qfan = (@Max Qexhaust Qsupply)')
if Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019')
if Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2022')
infil_program.addLine('Set Qimb = (@Abs (Qsupply - Qexhaust))')
infil_program.addLine('Set Qinf_adj = (Qinf^2) / (Qinf + Qimb)')
elsif Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2019')
# Follow ASHRAE 62.2-2016, Normative Appendix C equations for time-varying total airflow
infil_program.addLine('If Qfan > 0')
# Balanced system if the total supply airflow and total exhaust airflow are within 10% of their average.
Expand Down Expand Up @@ -2018,12 +2016,12 @@ def self.get_infiltration_SLA_from_ACH(ach, infil_height, avg_ceiling_height, we

def self.get_infiltration_SLA_from_ACH50(ach50, n_i, floor_area, volume)
# Returns the infiltration SLA given a ACH50.
return ((ach50 * 0.283316478 * 4.0**n_i * volume) / (floor_area * UnitConversions.convert(1.0, 'ft^2', 'in^2') * 50.0**n_i * 60.0))
return ((ach50 * 0.283316 * 4.0**n_i * volume) / (floor_area * UnitConversions.convert(1.0, 'ft^2', 'in^2') * 50.0**n_i * 60.0))
end

def self.get_infiltration_ACH50_from_SLA(sla, n_i, floor_area, volume)
# Returns the infiltration ACH50 given a SLA.
return ((sla * floor_area * UnitConversions.convert(1.0, 'ft^2', 'in^2') * 50.0**n_i * 60.0) / (0.283316478 * 4.0**n_i * volume))
return ((sla * floor_area * UnitConversions.convert(1.0, 'ft^2', 'in^2') * 50.0**n_i * 60.0) / (0.283316 * 4.0**n_i * volume))
end

def self.get_infiltration_Qinf_from_NL(nl, weather, cfa)
Expand Down Expand Up @@ -2103,6 +2101,47 @@ def self.get_mech_vent_qtot_cfm(nbeds, cfa)
# Returns Qtot cfm per ASHRAE 62.2-2019
return (nbeds + 1.0) * 7.5 + 0.03 * cfa
end

def self.get_mech_vent_qfan_cfm(q_tot, q_inf, is_balanced, frac_imbal, a_ext, bldg_type, eri_version, hours_in_operation)
q_inf_eff = q_inf * a_ext
if Constants.ERIVersions.index(eri_version) >= Constants.ERIVersions.index('2022')
if frac_imbal == 0
q_fan = q_tot - q_inf_eff
else
q_inf_eff = q_inf * a_ext
if q_inf_eff >= q_tot
q_fan = 0.0
else
q_fan = ((frac_imbal**2.0 * q_tot**2.0 - 4.0 * frac_imbal * q_inf_eff**2.0 + 2.0 * frac_imbal * q_inf_eff * q_tot + q_inf_eff**2.0)**0.5 + frac_imbal * q_tot - q_inf_eff) / (2.0 * frac_imbal)
end
end
elsif 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_eff
else
if [HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include? bldg_type
# No infiltration credit for attached/multifamily
return q_tot
end

if q_inf > 2.0 / 3.0 * q_tot
q_fan = q_tot - 2.0 / 3.0 * q_tot
else
q_fan = q_tot - q_inf
end
end

# Convert from hourly average requirement to actual fan flow rate
if not hours_in_operation.nil?
q_fan *= 24.0 / hours_in_operation
end

return [q_fan, 0.0].max
end
end

class Duct
Expand Down
2 changes: 1 addition & 1 deletion HPXMLtoOpenStudio/resources/hpxml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2344,7 +2344,7 @@ def to_doc(building)
fail "Unhandled attic type '#{@attic_type}'."
end
end
XMLHelper.add_element(attic, 'WithinInfiltrationVolume', within_infiltration_volume, :boolean) unless @within_infiltration_volume.nil?
XMLHelper.add_element(attic, 'WithinInfiltrationVolume', @within_infiltration_volume, :boolean, @within_infiltration_volume_isdefaulted) unless @within_infiltration_volume.nil?
if not @attached_to_roof_idrefs.nil?
@attached_to_roof_idrefs.each do |roof|
roof_attached = XMLHelper.add_element(attic, 'AttachedToRoof')
Expand Down
64 changes: 43 additions & 21 deletions HPXMLtoOpenStudio/resources/hpxml_defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def self.apply(runner, hpxml, hpxml_bldg, eri_version, weather, epw_file: nil, s
apply_building_occupancy(hpxml_bldg, schedules_file)
apply_building_construction(hpxml_bldg, cfa, nbeds)
apply_climate_and_risk_zones(hpxml_bldg, epw_file)
apply_infiltration(hpxml_bldg)
apply_attics(hpxml_bldg)
apply_foundations(hpxml_bldg)
apply_infiltration(hpxml_bldg)
apply_roofs(hpxml_bldg)
apply_rim_joists(hpxml_bldg)
apply_walls(hpxml_bldg)
Expand Down Expand Up @@ -699,27 +699,16 @@ def self.apply_climate_and_risk_zones(hpxml_bldg, epw_file)
end
end

def self.apply_infiltration(hpxml_bldg)
infil_measurement = Airflow.get_infiltration_measurement_of_interest(hpxml_bldg.air_infiltration_measurements)
if infil_measurement.infiltration_volume.nil?
infil_measurement.infiltration_volume = hpxml_bldg.building_construction.conditioned_building_volume
infil_measurement.infiltration_volume_isdefaulted = true
end
if infil_measurement.infiltration_height.nil?
infil_measurement.infiltration_height = hpxml_bldg.inferred_infiltration_height(infil_measurement.infiltration_volume)
infil_measurement.infiltration_height_isdefaulted = true
end
if infil_measurement.a_ext.nil?
if (infil_measurement.infiltration_type == HPXML::InfiltrationTypeUnitTotal) &&
[HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include?(hpxml_bldg.building_construction.residential_facility_type)
tot_cb_area, ext_cb_area = hpxml_bldg.compartmentalization_boundary_areas()
infil_measurement.a_ext = (ext_cb_area / tot_cb_area).round(5)
infil_measurement.a_ext_isdefaulted = true
def self.apply_attics(hpxml_bldg)
hpxml_bldg.attics.each do |attic|
next unless attic.within_infiltration_volume.nil?

if [HPXML::AtticTypeUnvented].include? attic.attic_type
attic.within_infiltration_volume = false
attic.within_infiltration_volume_isdefaulted = true
end
end
end

def self.apply_attics(hpxml_bldg)
return unless hpxml_bldg.has_location(HPXML::LocationAtticVented)

vented_attics = hpxml_bldg.attics.select { |a| a.attic_type == HPXML::AtticTypeVented }
Expand All @@ -738,6 +727,16 @@ def self.apply_attics(hpxml_bldg)
end

def self.apply_foundations(hpxml_bldg)
hpxml_bldg.foundations.each do |foundation|
next unless foundation.within_infiltration_volume.nil?

next unless [HPXML::FoundationTypeBasementUnconditioned,
HPXML::FoundationTypeCrawlspaceUnvented].include? foundation.foundation_type

foundation.within_infiltration_volume = false
foundation.within_infiltration_volume_isdefaulted = true
end

if hpxml_bldg.has_location(HPXML::LocationCrawlspaceVented)
vented_crawls = hpxml_bldg.foundations.select { |f| f.foundation_type == HPXML::FoundationTypeCrawlspaceVented }
if vented_crawls.empty?
Expand Down Expand Up @@ -771,6 +770,26 @@ def self.apply_foundations(hpxml_bldg)
end
end

def self.apply_infiltration(hpxml_bldg)
infil_measurement = Airflow.get_infiltration_measurement_of_interest(hpxml_bldg.air_infiltration_measurements)
if infil_measurement.infiltration_volume.nil?
infil_measurement.infiltration_volume = hpxml_bldg.building_construction.conditioned_building_volume
infil_measurement.infiltration_volume_isdefaulted = true
end
if infil_measurement.infiltration_height.nil?
infil_measurement.infiltration_height = hpxml_bldg.inferred_infiltration_height(infil_measurement.infiltration_volume)
infil_measurement.infiltration_height_isdefaulted = true
end
if infil_measurement.a_ext.nil?
if (infil_measurement.infiltration_type == HPXML::InfiltrationTypeUnitTotal) &&
[HPXML::ResidentialTypeApartment, HPXML::ResidentialTypeSFA].include?(hpxml_bldg.building_construction.residential_facility_type)
tot_cb_area, ext_cb_area = hpxml_bldg.compartmentalization_boundary_areas()
infil_measurement.a_ext = (ext_cb_area / tot_cb_area).round(5)
infil_measurement.a_ext_isdefaulted = true
end
end
end

def self.apply_roofs(hpxml_bldg)
hpxml_bldg.roofs.each do |roof|
if roof.azimuth.nil?
Expand Down Expand Up @@ -2052,18 +2071,21 @@ def self.apply_ventilation_fans(hpxml_bldg, weather, cfa, nbeds, eri_version)
vent_fan.is_shared_system = false
vent_fan.is_shared_system_isdefaulted = true
end

if vent_fan.hours_in_operation.nil? && !vent_fan.is_cfis_supplemental_fan?
vent_fan.hours_in_operation = (vent_fan.fan_type == HPXML::MechVentTypeCFIS) ? 8.0 : 24.0
vent_fan.hours_in_operation_isdefaulted = true
end
if vent_fan.rated_flow_rate.nil? && vent_fan.tested_flow_rate.nil? && vent_fan.calculated_flow_rate.nil? && vent_fan.delivered_ventilation.nil?

if vent_fan.flow_rate.nil?
if hpxml_bldg.ventilation_fans.select { |vf| vf.used_for_whole_building_ventilation && !vf.is_cfis_supplemental_fan? }.size > 1
fail 'Defaulting flow rates for multiple mechanical ventilation systems is currently not supported.'
end

vent_fan.rated_flow_rate = Airflow.get_default_mech_vent_flow_rate(hpxml_bldg, vent_fan, weather, cfa, nbeds).round(1)
vent_fan.rated_flow_rate = Airflow.get_default_mech_vent_flow_rate(hpxml_bldg, vent_fan, weather, cfa, nbeds, eri_version).round(1)
vent_fan.rated_flow_rate_isdefaulted = true
end

if vent_fan.fan_power.nil?
vent_fan.fan_power = (vent_fan.flow_rate * Airflow.get_default_mech_vent_fan_power(vent_fan, eri_version)).round(1)
vent_fan.fan_power_isdefaulted = true
Expand Down
Loading

0 comments on commit aaf1a63

Please sign in to comment.