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

ControlType input for CFIS systems #1866

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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