Skip to content

Commit

Permalink
Merge pull request #1660 from NREL/ansi_301_2022_cfis
Browse files Browse the repository at this point in the history
CFIS w/ AdditionalRuntimeOperatingMode="none"
  • Loading branch information
shorowit committed Jul 2, 2024
2 parents e10e41e + 3bf2d33 commit 610d1f5
Show file tree
Hide file tree
Showing 16 changed files with 917 additions and 57 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

__New Features__
- Adds inputs for modeling skylight curbs and/or shafts.
- Central Fan Integrated Supply (CFIS) mechanical ventilation enhancements:
- Allows modeling systems with no strategy to meet remainder of ventilation target (`CFISControls/AdditionalRuntimeOperatingMode="none"`).
- HVAC Manual J design load and sizing calculations:
- Adds optional inputs and outputs for blower fan heat and piping load.
- Miscellaneous improvements.
Expand Down
8 changes: 4 additions & 4 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>423dde30-61b6-4cc2-bbb6-e14b436faac7</version_id>
<version_modified>2024-07-02T00:19:28Z</version_modified>
<version_id>c249bd31-bb0e-49dd-9d2a-8a16e89b7d76</version_id>
<version_modified>2024-07-02T21:37:44Z</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>11C5D923</checksum>
<checksum>8BAD739B</checksum>
</file>
<file>
<filename>battery.rb</filename>
Expand Down Expand Up @@ -369,7 +369,7 @@
<filename>hpxml_schematron/EPvalidator.xml</filename>
<filetype>xml</filetype>
<usage_type>resource</usage_type>
<checksum>AE34574D</checksum>
<checksum>0E1E5211</checksum>
</file>
<file>
<filename>hpxml_schematron/iso-schematron.xsd</filename>
Expand Down
55 changes: 26 additions & 29 deletions HPXMLtoOpenStudio/resources/airflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1728,60 +1728,57 @@ def self.apply_cfis(infil_program, vent_mech_fans, cfis_fan_actuator, cfis_suppl
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = 0") # New hour, time on summation re-initializes to 0
infil_program.addLine('EndIf')

cfis_open_time = [vent_mech.hours_in_operation / 24.0 * 60.0, 59.999].min # Minimum open time in minutes
infil_program.addLine("Set cfis_t_min_hr_open = #{cfis_open_time}") # minutes per hour the CFIS damper is open
infil_program.addLine("Set cfis_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 cfis_Q_duct_oa = #{UnitConversions.convert(vent_mech.oa_unit_flow_rate, 'cfm', 'm^3/s')}")
infil_program.addLine('Set cfis_f_damper_open = 0') # fraction of the timestep the CFIS damper is open
infil_program.addLine("Set #{@cfis_f_damper_extra_open_var[vent_mech.id].name} = 0") # additional runtime fraction to meet min/hr
infil_program.addLine("Set cfis_has_additional_runtime = #{vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeNone ? 0 : 1}")

infil_program.addLine("If #{@cfis_t_sum_open_var[vent_mech.id].name} < cfis_t_min_hr_open")
infil_program.addLine(" Set cfis_t_fan_on = 60 - (cfis_t_min_hr_open - #{@cfis_t_sum_open_var[vent_mech.id].name})") # minute at which the blower needs to turn on to meet the ventilation requirements
# Evaluate condition of whether supply fan has to run to achieve target minutes per hour of operation
infil_program.addLine(' If (Minute+0.00001) >= cfis_t_fan_on')
# Consider fan rtf read in current calling point (results of previous time step) + cfis_t_fan_on based on min/hr requirement and previous EMS results.
infil_program.addLine(' Set cfis_fan_runtime = @Max (@ABS(Minute - cfis_t_fan_on)) (fan_rtf_hvac * ZoneTimeStep * 60)')
# If fan_rtf_hvac, make sure it's not exceeding ventilation requirements
infil_program.addLine(" Set cfis_fan_runtime = @Min cfis_fan_runtime (cfis_t_min_hr_open - #{@cfis_t_sum_open_var[vent_mech.id].name})")
infil_program.addLine(' Set cfis_f_damper_open = cfis_fan_runtime/(60.0*ZoneTimeStep)') # calculates the portion of the current timestep the CFIS damper needs to be open
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = #{@cfis_t_sum_open_var[vent_mech.id].name}+cfis_fan_runtime")
infil_program.addLine(" Set #{@cfis_f_damper_extra_open_var[vent_mech.id].name} = @Max (cfis_f_damper_open-fan_rtf_hvac) 0.0")
infil_program.addLine(" Set cfis_t_fan_on = 60 - (cfis_t_min_hr_open - #{@cfis_t_sum_open_var[vent_mech.id].name})") # Minute of the hour at which the blower needs to turn on to meet the ventilation requirements
infil_program.addLine(' If ((Minute+0.00001) >= cfis_t_fan_on) && (cfis_has_additional_runtime == 1)') # Evaluate condition of whether supply fan has to run to achieve target minutes per hour of operation
infil_program.addLine(' Set cfis_fan_runtime = @Max (@ABS(Minute - cfis_t_fan_on)) (fan_rtf_hvac * ZoneTimeStep * 60)') # Consider fan rtf read in current calling point (results of previous time step) + cfis_t_fan_on based on min/hr requirement and previous EMS results.
infil_program.addLine(" Set cfis_fan_runtime = @Min cfis_fan_runtime (cfis_t_min_hr_open - #{@cfis_t_sum_open_var[vent_mech.id].name})") # If fan_rtf_hvac, make sure it's not exceeding ventilation requirements
infil_program.addLine(' Set cfis_f_damper_open = cfis_fan_runtime / (60.0 * ZoneTimeStep)') # calculates the portion of the current timestep the CFIS damper needs to be open
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = #{@cfis_t_sum_open_var[vent_mech.id].name} + cfis_fan_runtime")
infil_program.addLine(" Set #{@cfis_f_damper_extra_open_var[vent_mech.id].name} = @Max (cfis_f_damper_open - fan_rtf_hvac) 0.0")
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} + cfis_fan_w*#{@cfis_f_damper_extra_open_var[vent_mech.id].name}")
infil_program.addLine(" Set #{cfis_fan_actuator.name} = #{cfis_fan_actuator.name} + cfis_fan_w * #{@cfis_f_damper_extra_open_var[vent_mech.id].name}")
elsif vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate < vent_mech.average_total_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_total_unit_flow_rate} cfm).")
end
infil_program.addLine(" Set cfis_suppl_Q_oa = #{UnitConversions.convert(vent_mech.cfis_supplemental_fan.oa_unit_flow_rate, 'cfm', 'm^3/s')}")
infil_program.addLine(" Set cfis_suppl_f = #{@cfis_f_damper_extra_open_var[vent_mech.id].name} / (cfis_suppl_Q_oa / cfis_Q_duct_oa)") # Calculate desired runtime for supplemental fan to provide remaining ventilation requirement
infil_program.addLine(' Set cfis_suppl_f = @Min cfis_suppl_f 1.0') # Ensure desired runtime does not exceed 100% (if the supplemental fan is undersized)
if vent_mech.cfis_supplemental_fan.oa_unit_flow_rate > 0
infil_program.addLine(" Set cfis_suppl_f = #{@cfis_f_damper_extra_open_var[vent_mech.id].name} / (cfis_suppl_Q_oa / cfis_Q_duct_oa)") # Calculate desired runtime for supplemental fan to provide remaining ventilation requirement
infil_program.addLine(' Set cfis_suppl_f = @Min cfis_suppl_f 1.0') # Ensure desired runtime does not exceed 100% (if the supplemental fan is undersized)
else
infil_program.addLine(' Set cfis_suppl_f = 0.0')
end
infil_program.addLine(" Set cfis_suppl_fan_w = #{vent_mech.cfis_supplemental_fan.unit_fan_power}") # W
infil_program.addLine(" Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + cfis_suppl_fan_w*cfis_suppl_f")
infil_program.addLine(" Set #{cfis_suppl_fan_actuator.name} = #{cfis_suppl_fan_actuator.name} + cfis_suppl_fan_w * cfis_suppl_f")
if vent_mech.cfis_supplemental_fan.fan_type == HPXML::MechVentTypeSupply
infil_program.addLine(' Set QWHV_cfis_suppl_sup = QWHV_cfis_suppl_sup + cfis_suppl_f * cfis_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 + cfis_suppl_f * cfis_suppl_Q_oa')
end
end
infil_program.addLine(' Else')
# No need to turn on blower for extra ventilation
infil_program.addLine(' Set cfis_fan_runtime = fan_rtf_hvac*ZoneTimeStep*60')
infil_program.addLine(" If (#{@cfis_t_sum_open_var[vent_mech.id].name}+cfis_fan_runtime) > cfis_t_min_hr_open")
# Damper is only open for a portion of this time step to achieve target minutes per hour
infil_program.addLine(" Set cfis_fan_runtime = cfis_t_min_hr_open-#{@cfis_t_sum_open_var[vent_mech.id].name}")
infil_program.addLine(' Set cfis_f_damper_open = cfis_fan_runtime/(ZoneTimeStep*60)')
infil_program.addLine(' Else') # No need to turn on blower for extra ventilation
infil_program.addLine(' Set cfis_fan_runtime = fan_rtf_hvac * ZoneTimeStep * 60')
infil_program.addLine(" If (#{@cfis_t_sum_open_var[vent_mech.id].name} + cfis_fan_runtime) > cfis_t_min_hr_open") # Damper is only open for a portion of this time step to achieve target minutes per hour
infil_program.addLine(" Set cfis_fan_runtime = cfis_t_min_hr_open - #{@cfis_t_sum_open_var[vent_mech.id].name}")
infil_program.addLine(' Set cfis_f_damper_open = cfis_fan_runtime / (ZoneTimeStep * 60)')
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = cfis_t_min_hr_open")
infil_program.addLine(' Else')
# Damper is open and using call for heat/cool to supply fresh air
infil_program.addLine(' Set cfis_fan_runtime = fan_rtf_hvac*ZoneTimeStep*60')
infil_program.addLine(' Else') # Damper is open and using call for heat/cool to supply fresh air
infil_program.addLine(' Set cfis_fan_runtime = fan_rtf_hvac * ZoneTimeStep * 60')
infil_program.addLine(' Set cfis_f_damper_open = fan_rtf_hvac')
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = #{@cfis_t_sum_open_var[vent_mech.id].name}+cfis_fan_runtime")
infil_program.addLine(" Set #{@cfis_t_sum_open_var[vent_mech.id].name} = #{@cfis_t_sum_open_var[vent_mech.id].name} + cfis_fan_runtime")
infil_program.addLine(' EndIf')
infil_program.addLine(' EndIf')

if vent_mech.cfis_addtl_runtime_operating_mode == HPXML::CFISModeSupplementalFan
infil_program.addLine(" Set cfis_f_damper_open = @Max (cfis_f_damper_open-#{@cfis_f_damper_extra_open_var[vent_mech.id].name}) 0.0")
else
infil_program.addLine(" Set cfis_f_damper_open = @Max (cfis_f_damper_open - #{@cfis_f_damper_extra_open_var[vent_mech.id].name}) 0.0")
end
infil_program.addLine(' Set QWHV_cfis_sup = QWHV_cfis_sup + cfis_f_damper_open * cfis_Q_duct_oa')

Expand Down
9 changes: 8 additions & 1 deletion HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1950,7 +1950,7 @@
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Systems/h:MechanicalVentilation/h:VentilationFans/h:VentilationFan[h:UsedForWholeBuildingVentilation="true" and h:FanType="central fan integrated supply"]'>
<sch:assert role='ERROR' test='count(h:IsSharedSystem[text()="true"]) = 0'>Expected 0 element(s) for xpath: IsSharedSystem[text()="true"]</sch:assert>
<sch:assert role='ERROR' test='count(h:CFISControls/h:AdditionalRuntimeOperatingMode) &lt;= 1'>Expected 0 or 1 element(s) for xpath: CFISControls/AdditionalRuntimeOperatingMode</sch:assert>
<sch:assert role='ERROR' test='h:CFISControls/h:AdditionalRuntimeOperatingMode[text()="air handler fan" or text()="supplemental fan"] or not(h:CFISControls/h:AdditionalRuntimeOperatingMode)'>Expected CFISControls/AdditionalRuntimeOperatingMode to be 'air handler fan' or 'supplemental fan'</sch:assert> <!-- See [MechanicalVentilationType=CFISWithSupplementalFan] -->
<sch:assert role='ERROR' test='h:CFISControls/h:AdditionalRuntimeOperatingMode[text()="air handler fan" or text()="supplemental fan" or text()="none"] or not(h:CFISControls/h:AdditionalRuntimeOperatingMode)'>Expected CFISControls/AdditionalRuntimeOperatingMode to be 'air handler fan' or 'supplemental fan' or 'none'</sch:assert> <!-- See [MechanicalVentilationType=CFISWithSupplementalFan] -->
<sch:assert role='ERROR' test='count(h:RatedFlowRate) + count(h:CalculatedFlowRate) + count(h:TestedFlowRate) + count(h:DeliveredVentilation) &gt;= 0'>Expected 0 or more element(s) for xpath: RatedFlowRate | CalculatedFlowRate | TestedFlowRate | DeliveredVentilation</sch:assert>
<sch:assert role='ERROR' test='count(h:HoursInOperation) &lt;= 1'>Expected 0 or 1 element(s) for xpath: HoursInOperation</sch:assert>
<sch:assert role='ERROR' test='count(h:TotalRecoveryEfficiency) + count(h:AdjustedTotalRecoveryEfficiency) = 0'>Expected 0 element(s) for xpath: TotalRecoveryEfficiency | AdjustedTotalRecoveryEfficiency</sch:assert>
Expand All @@ -1970,6 +1970,13 @@
</sch:rule>
</sch:pattern>

<sch:pattern>
<sch:title>[MechanicalVentilationType=CFISWithoutSupplementalFan]</sch:title>
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Systems/h:MechanicalVentilation/h:VentilationFans/h:VentilationFan[h:UsedForWholeBuildingVentilation="true" and h:FanType="central fan integrated supply"]/h:CFISControls[h:AdditionalRuntimeOperatingMode!="supplemental fan"]'>
<sch:assert role='ERROR' test='count(h:SupplementalFan) = 0'>Expected 0 element(s) for xpath: SupplementalFan</sch:assert>
</sch:rule>
</sch:pattern>

<sch:pattern>
<sch:title>[MechanicalVentilationType=Shared]</sch:title>
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Systems/h:MechanicalVentilation/h:VentilationFans/h:VentilationFan[h:UsedForWholeBuildingVentilation="true" and h:IsSharedSystem="true"]'>
Expand Down
4 changes: 2 additions & 2 deletions docs/source/workflow_inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3456,7 +3456,7 @@ Each central fan integrated supply (CFIS) system is entered as a ``/HPXML/Buildi
============================================================================================= ======== ======= ============================= ======== =============== =========================================

.. [#] All other UsedFor... elements (i.e., ``UsedForLocalVentilation``, ``UsedForSeasonalCoolingLoadReduction``, ``UsedForGarageVentilation``) must be omitted or false.
.. [#] AdditionalRuntimeOperatingMode choices are "air handler fan" or "supplemental fan".
.. [#] AdditionalRuntimeOperatingMode choices are "air handler fan", "supplemental fan", or "none".
.. [#] SupplementalFan must reference another ``VentilationFan`` where UsedForWholeBuildingVentilation=true, IsSharedSystem=false, and FanType="exhaust only" or "supply only".
.. [#] SupplementalFan only required if AdditionalRuntimeOperatingMode is "supplemental fan".
.. [#] If flow rate not provided, defaults to the required mechanical ventilation rate per `ANSI/RESNET/ICC 301-2022 <https://codes.iccsafe.org/content/RESNET3012022P1>`_:
Expand All @@ -3476,7 +3476,7 @@ Each central fan integrated supply (CFIS) system is entered as a ``/HPXML/Buildi
OpenStudio-HPXML does not currently support defaulting flow rates for multiple mechanical ventilation fans.
.. [#] The flow rate should equal the amount of outdoor air provided to the distribution system, not the total airflow through the distribution system.
.. [#] The HoursInOperation and the flow rate are combined to form the hourly target ventilation rate (e.g., inputs of 90 cfm and 8 hrs/day produce an hourly target ventilation rate of 30 cfm).
.. [#] HoursInOperation is combined with the flow rate to form the hourly target ventilation rate (e.g., inputs of 90 cfm and 8 hrs/day produce an hourly target ventilation rate of 30 cfm).
.. [#] If FanPower not provided, defaults to 0.58 W/cfm based on ANSI/RESNET/ICC 301-2022 Addendum C.
.. [#] HVACDistribution type cannot be :ref:`hvac_distribution_hydronic`.
.. [#] Blower airflow rate when operating in ventilation only mode (i.e., not heating or cooling mode), as a fraction of the maximum blower airflow rate.
Expand Down
2 changes: 2 additions & 0 deletions tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,8 @@ def apply_hpxml_modification_sample_files(hpxml_path, hpxml)
used_for_whole_building_ventilation: true)
elsif ['base-mechvent-cfis-airflow-fraction-zero.xml'].include? hpxml_file
hpxml_bldg.ventilation_fans[0].cfis_vent_mode_airflow_fraction = 0.0
elsif ['base-mechvent-cfis-no-additional-runtime.xml'].include? hpxml_file
hpxml_bldg.ventilation_fans[0].cfis_addtl_runtime_operating_mode = HPXML::CFISModeNone
elsif ['base-mechvent-cfis-supplemental-fan-exhaust.xml',
'base-mechvent-cfis-supplemental-fan-supply.xml'].include? hpxml_file
hpxml_bldg.ventilation_fans.add(id: "VentilationFan#{hpxml_bldg.ventilation_fans.size + 1}",
Expand Down
3 changes: 3 additions & 0 deletions workflow/hpxml_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3065,6 +3065,9 @@
"mech_vent_fan_power": 300,
"mech_vent_num_units_served": 1
},
"sample_files/base-mechvent-cfis-no-additional-runtime.xml": {
"parent_hpxml": "sample_files/base-mechvent-cfis.xml"
},
"sample_files/base-mechvent-cfis-supplemental-fan-exhaust.xml": {
"parent_hpxml": "sample_files/base-mechvent-cfis.xml"
},
Expand Down
Loading

0 comments on commit 610d1f5

Please sign in to comment.