Skip to content

Commit

Permalink
Merge pull request #1866 from NREL/cfis_control_type
Browse files Browse the repository at this point in the history
`ControlType` input for CFIS systems
  • Loading branch information
shorowit authored Oct 23, 2024
2 parents b7efd03 + c342b13 commit 9e0f85f
Show file tree
Hide file tree
Showing 18 changed files with 707 additions and 78 deletions.
7 changes: 4 additions & 3 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ __New Features__
- Updates relationship between number of bedrooms and number of occupants to use RECS 2020 instead of RECS 2015.
- Allows optional `HeatingCapacity` and `BackupHeatingCapacity` inputs for heat pump water heaters (HPWHs).
- Central Fan Integrated Supply (CFIS) mechanical ventilation enhancements:
- CFIS systems without automatic flow control of outdoor air (`CFISControls/HasOutdoorAirControl=false`).
- CFIS systems with no strategy to meet remainder of ventilation target (`CFISControls/AdditionalRuntimeOperatingMode="none"`).
- CFIS systems with supplemental fans that run simultaneously with the air handler (`CFISControls/extension/SupplementalFanRunsWithAirHandlerFan=true`).
- Allows CFIS systems without automatic flow control of outdoor air (`CFISControls/HasOutdoorAirControl=false`).
- Allows CFIS systems with no strategy to meet remainder of ventilation target (`CFISControls/AdditionalRuntimeOperatingMode="none"`).
- Allows CFIS systems with supplemental fans that run simultaneously with the air handler (`CFISControls/extension/SupplementalFanRunsWithAirHandlerFan=true`).
- Allows CFIS systems with timer control, in which ventilation operation occurs at a fixed interval (`CFISControls/extension/ControlType="timer"`).
- HVAC Manual J design load and sizing calculations:
- Adds optional `DistributionSystemType/AirDistribution/extension/ManualJInputs/BlowerFanHeatBtuh` input.
- Adds optional `DistributionSystemType/HydronicDistribution/extension/ManualJInputs/HotWaterPipingBtuh` input.
Expand Down
18 changes: 9 additions & 9 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>6f307da4-b354-4c31-897f-de2161a9de82</version_id>
<version_modified>2024-10-22T21:49:52Z</version_modified>
<version_id>a34fa4e4-deee-4e59-8f0b-8c1a2c574cdc</version_id>
<version_modified>2024-10-23T18:46:26Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -189,7 +189,7 @@
<filename>airflow.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>FFA79541</checksum>
<checksum>F2AD4FE1</checksum>
</file>
<file>
<filename>battery.rb</filename>
Expand Down Expand Up @@ -327,7 +327,7 @@
<filename>defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>E52E98C6</checksum>
<checksum>0329B068</checksum>
</file>
<file>
<filename>energyplus.rb</filename>
Expand Down Expand Up @@ -357,7 +357,7 @@
<filename>hpxml.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>656CF61F</checksum>
<checksum>E00013AE</checksum>
</file>
<file>
<filename>hpxml_schema/HPXML.xsd</filename>
Expand All @@ -375,7 +375,7 @@
<filename>hpxml_schematron/EPvalidator.xml</filename>
<filetype>xml</filetype>
<usage_type>resource</usage_type>
<checksum>DD7BB2F9</checksum>
<checksum>AC4BF61E</checksum>
</file>
<file>
<filename>hpxml_schematron/iso-schematron.xsd</filename>
Expand Down Expand Up @@ -651,7 +651,7 @@
<filename>test_airflow.rb</filename>
<filetype>rb</filetype>
<usage_type>test</usage_type>
<checksum>D1425F08</checksum>
<checksum>D4E42045</checksum>
</file>
<file>
<filename>test_battery.rb</filename>
Expand All @@ -663,7 +663,7 @@
<filename>test_defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>test</usage_type>
<checksum>77AFEE2C</checksum>
<checksum>1531AC7C</checksum>
</file>
<file>
<filename>test_enclosure.rb</filename>
Expand Down Expand Up @@ -735,7 +735,7 @@
<filename>test_validation.rb</filename>
<filetype>rb</filetype>
<usage_type>test</usage_type>
<checksum>7CB39E62</checksum>
<checksum>20E56F9D</checksum>
</file>
<file>
<filename>test_water_heater.rb</filename>
Expand Down
121 changes: 65 additions & 56 deletions HPXMLtoOpenStudio/resources/airflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1986,79 +1986,88 @@ def self.apply_cfis(runner, infil_program, vent_mech_fans, cfis_data, cfis_fan_a

infil_program.addLine("Set t_min_hr_open = #{[vent_mech.hours_in_operation / 24.0 * 60.0, 59.999].min}") # Minimum CFIS damper open time in minutes
infil_program.addLine("Set Q_duct_oa = #{UnitConversions.convert(vent_mech.oa_unit_flow_rate, 'cfm', 'm^3/s')}")
infil_program.addLine('Set f_open_damper = 0')
infil_program.addLine("Set #{f_vent_only_mode_var.name} = 0")
infil_program.addLine("Set has_additional_runtime = #{vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeNone ? 0 : 1}")
infil_program.addLine("Set has_outdoor_air_control = #{vent_mech.cfis_has_outdoor_air_control ? 1 : 0}")
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
infil_program.addLine("Set suppl_fan_w = #{vent_mech.cfis_supplemental_fan.unit_fan_power}") # W
infil_program.addLine("Set suppl_fan_w = #{vent_mech.cfis_supplemental_fan.unit_fan_power}") # W, fan power for additional ventilation only mode runtime
elsif vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeAirHandler
infil_program.addLine("Set ah_fan_w = #{vent_mech.unit_fan_power}") # W, fan power for additional ventilation only mode runtime
end

infil_program.addLine("If #{t_sum_open_damper_var.name} < t_min_hr_open") # Check whether we've met the minimum hourly runtime
infil_program.addLine(" Set t_damper_open = 60 - (t_min_hr_open - #{t_sum_open_damper_var.name})") # Minute of the hour at which the damper must be opened
infil_program.addLine(' If ((Minute+0.00001) >= t_damper_open) && (has_additional_runtime == 1)') # Check whether the damper must be opened to achieve target minutes per hour of operation
infil_program.addLine(' Set open_damper_runtime = @Max (@ABS(Minute - t_damper_open)) (fan_rtf_hvac * ZoneTimeStep * 60)') # How many minutes this timestep the damper is open
infil_program.addLine(" Set open_damper_runtime = @Min open_damper_runtime (t_min_hr_open - #{t_sum_open_damper_var.name})") # Make sure it's not exceeding target ventilation
infil_program.addLine(" Set #{t_sum_open_damper_var.name} = #{t_sum_open_damper_var.name} + open_damper_runtime") # How many minutes this hour the damper is open
infil_program.addLine(' Set f_open_damper = open_damper_runtime / (60.0 * ZoneTimeStep)') # Fraction of the timestep that the damper is open
infil_program.addLine(" Set #{f_vent_only_mode_var.name} = @Max (f_open_damper - fan_rtf_hvac) 0.0") # Fraction of the timestep with ventilation only mode runtime
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeAirHandler
# Air handler meets additional runtime requirement
infil_program.addLine(" Set fan_w = #{vent_mech.unit_fan_power}") # W
infil_program.addLine(" Set #{cfis_fan_actuator.name} = #{cfis_fan_actuator.name} + fan_w * #{f_vent_only_mode_var.name}")
elsif vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
# Supplemental fan meets additional runtime requirement
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate < vent_mech.average_unit_flow_rate
runner.registerWarning("CFIS supplemental fan '#{vent_mech.cfis_supplemental_fan.id}' is undersized (#{vent_mech.cfis_supplemental_fan.oa_unit_flow_rate} cfm) compared to the target hourly ventilation rate (#{vent_mech.average_unit_flow_rate} cfm).")
if vent_mech.cfis_control_type == HPXML::CFISControlTypeTimer
# Ventilation occurs at fixed intervals regardless of HVAC operation
infil_program.addLine('Set f_open_damper_ah = t_min_hr_open/60')
infil_program.addLine("Set #{f_vent_only_mode_var.name} = f_open_damper_ah * (1 - fan_rtf_hvac)") # Fraction of the timestep with ventilation only mode runtime per ANSI 301-2022 Addendum E
infil_program.addLine("Set #{cfis_fan_actuator.name} = #{cfis_fan_actuator.name} + (ah_fan_w * #{f_vent_only_mode_var.name})") # Fan energy associated with ventilation only mode runtime
elsif vent_mech.cfis_control_type == HPXML::CFISControlTypeOptimized
# Ventilation optimized to make use of HVAC operation
infil_program.addLine('Set f_open_damper = 0')
infil_program.addLine("Set has_additional_runtime = #{vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeNone ? 0 : 1}")
infil_program.addLine("If #{t_sum_open_damper_var.name} < t_min_hr_open") # Check whether we've met the minimum hourly runtime
infil_program.addLine(" Set t_damper_open = 60 - (t_min_hr_open - #{t_sum_open_damper_var.name})") # Minute of the hour at which the damper must be opened
infil_program.addLine(' If ((Minute+0.00001) >= t_damper_open) && (has_additional_runtime == 1)') # Check whether the damper must be opened to achieve target minutes per hour of operation
infil_program.addLine(' Set open_damper_runtime = @Max (@ABS(Minute - t_damper_open)) (fan_rtf_hvac * ZoneTimeStep * 60)') # How many minutes this timestep the damper is open
infil_program.addLine(" Set open_damper_runtime = @Min open_damper_runtime (t_min_hr_open - #{t_sum_open_damper_var.name})") # Make sure it's not exceeding target ventilation
infil_program.addLine(" Set #{t_sum_open_damper_var.name} = #{t_sum_open_damper_var.name} + open_damper_runtime") # How many minutes this hour the damper is open
infil_program.addLine(' Set f_open_damper = open_damper_runtime / (60.0 * ZoneTimeStep)') # Fraction of the timestep that the damper is open
infil_program.addLine(" Set #{f_vent_only_mode_var.name} = @Max (f_open_damper - fan_rtf_hvac) 0.0") # Fraction of the timestep with ventilation only mode runtime
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeAirHandler
# Air handler meets additional runtime requirement
infil_program.addLine(" Set #{cfis_fan_actuator.name} = #{cfis_fan_actuator.name} + ah_fan_w * #{f_vent_only_mode_var.name}") # Fan energy associated with ventilation only mode runtime
elsif vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
# Supplemental fan meets additional runtime requirement
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate < vent_mech.average_unit_flow_rate
runner.registerWarning("CFIS supplemental fan '#{vent_mech.cfis_supplemental_fan.id}' is undersized (#{vent_mech.cfis_supplemental_fan.oa_unit_flow_rate} cfm) compared to the target hourly ventilation rate (#{vent_mech.average_unit_flow_rate} cfm).")
end
infil_program.addLine(" Set suppl_Q_oa = #{UnitConversions.convert(vent_mech.cfis_supplemental_fan.oa_unit_flow_rate, 'cfm', 'm^3/s')}")
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate > 0
infil_program.addLine(" Set suppl_f = #{f_vent_only_mode_var.name} / (suppl_Q_oa / Q_duct_oa)") # Calculate desired runtime for supplemental fan to provide remaining ventilation requirement
infil_program.addLine(' Set suppl_f = @Min suppl_f 1.0') # Ensure desired runtime does not exceed 100% (if the supplemental fan is undersized)
else
infil_program.addLine(' Set suppl_f = 0.0')
end
infil_program.addLine(" Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + suppl_fan_w * suppl_f")
if vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeSupply
infil_program.addLine(' Set QWHV_cfis_suppl_sup = QWHV_cfis_suppl_sup + (suppl_f * suppl_Q_oa)')
elsif vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeExhaust
infil_program.addLine(' Set QWHV_cfis_suppl_exh = QWHV_cfis_suppl_exh + (suppl_f * suppl_Q_oa)')
end
end
infil_program.addLine(" Set suppl_Q_oa = #{UnitConversions.convert(vent_mech.cfis_supplemental_fan.oa_unit_flow_rate, 'cfm', 'm^3/s')}")
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate > 0
infil_program.addLine(" Set suppl_f = #{f_vent_only_mode_var.name} / (suppl_Q_oa / Q_duct_oa)") # Calculate desired runtime for supplemental fan to provide remaining ventilation requirement
infil_program.addLine(' Set suppl_f = @Min suppl_f 1.0') # Ensure desired runtime does not exceed 100% (if the supplemental fan is undersized)
infil_program.addLine(' Else') # No additional ventilation mode runtime
infil_program.addLine(' Set open_damper_runtime = fan_rtf_hvac * ZoneTimeStep * 60') # How many minutes this timestep the damper is open
infil_program.addLine(" If (#{t_sum_open_damper_var.name} + open_damper_runtime) > t_min_hr_open") # Damper is only open for a portion of this time step to achieve target ventilation
infil_program.addLine(" Set open_damper_runtime = t_min_hr_open - #{t_sum_open_damper_var.name}")
infil_program.addLine(' EndIf')
infil_program.addLine(' Set f_open_damper = open_damper_runtime / (ZoneTimeStep * 60)') # Fraction of the timestep that the damper is open
infil_program.addLine(" Set #{t_sum_open_damper_var.name} = #{t_sum_open_damper_var.name} + open_damper_runtime") # How many minutes this hour the damper is open
infil_program.addLine(' EndIf')
infil_program.addLine('EndIf')

# Calculate fraction of the timestep that the damper is open and the air handler fan is running
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
infil_program.addLine("Set f_open_damper_ah = @Max (f_open_damper - #{f_vent_only_mode_var.name}) 0.0")
else
infil_program.addLine(' Set suppl_f = 0.0')
infil_program.addLine('Set f_open_damper_ah = f_open_damper')
end
infil_program.addLine(" Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + suppl_fan_w * suppl_f")
if vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeSupply
infil_program.addLine(' Set QWHV_cfis_suppl_sup = QWHV_cfis_suppl_sup + (suppl_f * suppl_Q_oa)')
elsif vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeExhaust
infil_program.addLine(' Set QWHV_cfis_suppl_exh = QWHV_cfis_suppl_exh + (suppl_f * suppl_Q_oa)')
end
end
infil_program.addLine(' Else') # No additional ventilation mode runtime
infil_program.addLine(' Set open_damper_runtime = fan_rtf_hvac * ZoneTimeStep * 60') # How many minutes this timestep the damper is open
infil_program.addLine(" If (#{t_sum_open_damper_var.name} + open_damper_runtime) > t_min_hr_open") # Damper is only open for a portion of this time step to achieve target ventilation
infil_program.addLine(" Set open_damper_runtime = t_min_hr_open - #{t_sum_open_damper_var.name}")
infil_program.addLine(' EndIf')
infil_program.addLine(' Set f_open_damper = open_damper_runtime / (ZoneTimeStep * 60)') # Fraction of the timestep that the damper is open
infil_program.addLine(" Set #{t_sum_open_damper_var.name} = #{t_sum_open_damper_var.name} + open_damper_runtime") # How many minutes this hour the damper is open
infil_program.addLine(' EndIf')
infil_program.addLine('EndIf')

# Calculate fraction of the timestep that the damper is open and the air handler fan is running
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
infil_program.addLine("Set f_open_damper_ah = @Max (f_open_damper - #{f_vent_only_mode_var.name}) 0.0")
else
infil_program.addLine('Set f_open_damper_ah = f_open_damper')
if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan && vent_mech.cfis_supplemental_fan_runs_with_air_handler_fan
# Additionally run supplemental fan when damper is open and HVAC system is running
infil_program.addLine("Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + (suppl_fan_w * f_open_damper_ah)")
if vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeSupply
infil_program.addLine('Set QWHV_cfis_suppl_sup = QWHV_cfis_suppl_sup + (f_open_damper_ah * suppl_Q_oa)')
elsif vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeExhaust
infil_program.addLine('Set QWHV_cfis_suppl_exh = QWHV_cfis_suppl_exh + (f_open_damper_ah * suppl_Q_oa)')
end
end
end

# If no outdoor air control, then outdoor air is introduced for at least the entire time the HVAC system is running
infil_program.addLine("Set has_outdoor_air_control = #{vent_mech.cfis_has_outdoor_air_control ? 1 : 0}")
infil_program.addLine('If has_outdoor_air_control == 0')
infil_program.addLine(' Set f_open_damper_ah = @Max f_open_damper_ah fan_rtf_hvac')
infil_program.addLine('EndIf')

# Airflow brought in through air handler
infil_program.addLine('Set QWHV_cfis_sup = QWHV_cfis_sup + (f_open_damper_ah * Q_duct_oa)')

next unless vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan && vent_mech.cfis_supplemental_fan_runs_with_air_handler_fan

# Also run supplemental fan when damper is open and HVAC system is running
infil_program.addLine("Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + (suppl_fan_w * f_open_damper_ah)")
if vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeSupply
infil_program.addLine('Set QWHV_cfis_suppl_sup = QWHV_cfis_suppl_sup + (f_open_damper_ah * suppl_Q_oa)')
elsif vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeExhaust
infil_program.addLine('Set QWHV_cfis_suppl_exh = QWHV_cfis_suppl_exh + (f_open_damper_ah * suppl_Q_oa)')
end
end
end

Expand Down
4 changes: 4 additions & 0 deletions HPXMLtoOpenStudio/resources/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2759,6 +2759,10 @@ def self.apply_ventilation_fans(hpxml_bldg, weather, eri_version)
vent_fan.cfis_supplemental_fan_runs_with_air_handler_fan = false
vent_fan.cfis_supplemental_fan_runs_with_air_handler_fan_isdefaulted = true
end
if vent_fan.cfis_control_type.nil?
vent_fan.cfis_control_type = HPXML::CFISControlTypeOptimized
vent_fan.cfis_control_type_isdefaulted = true
end
end

# Default kitchen fan
Expand Down
Loading

0 comments on commit 9e0f85f

Please sign in to comment.