Skip to content

Conversation

@lymereJ
Copy link
Collaborator

@lymereJ lymereJ commented Dec 12, 2025

Pull request overview

Description of the purpose of this PR

The defect files highlighted two issues:

  1. The maximum heating runtime fraction (RTF) used by the airflow network to calculate the impact of cycling fan operations, when using an AirLoopHVAC:UnitaryHeatCool furnace-type system, is currently determined by the heating coil code. However, during the furnace simulation, the furnace's full load output is calculated with the heating coil's RTF == 1. As a result, the maximum heating RTF is always set to 1 for the airflow network calculations, which does not appropriately capture the impact of cycling fan operations.
  2. The AirLoopHVAC:UnitaryHeatCool nominal capacity does not account for the Sizing:System's Fraction of Autosized Heating Design Capacity when the Heating Design Capacity Method is set to FractionOfAutosizedHeatingCapacity, whereas the heating coil does.

This pull request fixes both issues.

Pull Request Author

  • Title of PR should be user-synopsis style (clearly understandable in a standalone changelog context)
  • Label the PR with at least one of: Defect, Refactoring, NewFeature, Performance, and/or DoNoPublish
  • Pull requests that impact EnergyPlus code must also include unit tests to cover enhancement or defect repair
  • Author should provide a "walkthrough" of relevant code changes using a GitHub code review comment process
  • If any diffs are expected, author must demonstrate they are justified using plots and descriptions
  • If changes fix a defect, the fix should be demonstrated in plots and descriptions
  • If structural output changes, add to output rules file and add OutputChange label

Reviewer

  • Perform a Code Review on GitHub
  • If branch is behind develop, merge develop and build locally to check for side effects of the merge
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified
  • Check that performance is not impacted (CI Linux results include performance check)
  • Run Unit Test(s) locally
  • Check any new function arguments for performance impacts
  • Verify IDF naming conventions and styles, memos and notes and defaults
  • If new idf included, locally check the err file and other outputs

@lymereJ lymereJ added this to the EnergyPlus 26.1 IOFreeze milestone Dec 12, 2025
@lymereJ lymereJ added Defect Includes code to repair a defect in EnergyPlus OutputChange Code changes output in such a way that it cannot be merged after IO freeze labels Dec 12, 2025
Copy link
Collaborator Author

@lymereJ lymereJ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes walk-through:

Comment on lines +6272 to +6278
SetupOutputVariable(m_state,
"AFN Zone Infiltration Mass Flow Rate",
Constant::Units::kg_s,
AirflowNetworkZnRpt(i).infilMassFlow,
OutputProcessor::TimeStepType::System,
OutputProcessor::StoreType::Average,
Zone(i).Name);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a new output variable so users don't have to manually calculate the mass flow rate from the ACH.

Comment on lines +6322 to +6327
if (state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).FractionOfAutosizedHeatingCapacity > 0) {
// apply sizing factor and reset it to 1.0 so it doesn't get apply again during heating coil sizing
thisFurnace.DesignHeatingCapacity *=
state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).FractionOfAutosizedHeatingCapacity;
state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).FractionOfAutosizedHeatingCapacity = 1.0;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System size is determined first, then heating coil size is determined after. In the former, the FractionOfAutosizedHeatingCapacity is not taken into account when sizing the system, but it is when sizing the coil. If the coil is part of a system, the system size is used for the coil and is modified by FractionOfAutosizedHeatingCapacity. Here we're applying FractionOfAutosizedHeatingCapacity to the system size and reset to 1 so it doesn't get used again to size the heating coil.

With develop:

 Component Sizing Information, AirLoopHVAC:UnitaryHeatCool, ACANDF_UNIT1, Nominal Heating Capacity [W], 8519.83583
 Component Sizing Information, Coil:Heating:Fuel, MAIN FUEL HEATING COIL_UNIT1, Design Size Nominal Capacity [W], 10649.79479

With this branch:

 Component Sizing Information, AirLoopHVAC:UnitaryHeatCool, ACANDF_UNIT1, Nominal Heating Capacity [W], 10649.79479
 Component Sizing Information, Coil:Heating:Fuel, MAIN FUEL HEATING COIL_UNIT1, Design Size Nominal Capacity [W], 10649.79479

Comment on lines +216 to +221
// Save the current AFNLoopHeatingCoilMaxRTF for comparison with the one calculated below
Real64 refAFNLoopHeatingCoilMaxRTF(0.0);
if (state.afn->distribution_simulated) {
refAFNLoopHeatingCoilMaxRTF = state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopHeatingCoilMaxRTF;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Save the maximum RTF that may have been previously determined from another component and use it later to determine the actual maximum RTF.

Comment on lines +669 to +703
// Get the actual maximum RTF for AFN simulations
if (state.afn->distribution_simulated) {
Real64 heatingCoilRTF = 0.0;
Real64 suppHeatingCoilRTF = 0.0;
bool errorFound(false);
if (thisFurnace.HeatingCoilType_Num == HVAC::Coil_HeatingGasOrOtherFuel ||
thisFurnace.HeatingCoilType_Num == HVAC::Coil_HeatingElectric || thisFurnace.HeatingCoilType_Num == HVAC::Coil_HeatingDesuperheater) {
int heatingCoilIndex;
HeatingCoils::GetCoilIndex(state, thisFurnace.HeatingCoilName, heatingCoilIndex, errorFound);
if (heatingCoilIndex > 0) {
heatingCoilRTF = state.dataHeatingCoils->HeatingCoil(heatingCoilIndex).RTF;
}
}
if (errorFound) {
ShowSevereError(state, format("The index of \"{}\" is not found", thisFurnace.HeatingCoilName));
ShowContinueError(state, format("...occurs for {}", thisFurnace.Name));
errorFound = false;
}
if (thisFurnace.SuppHeatCoilType_Num == HVAC::Coil_HeatingGasOrOtherFuel ||
thisFurnace.SuppHeatCoilType_Num == HVAC::Coil_HeatingElectric ||
thisFurnace.SuppHeatCoilType_Num == HVAC::Coil_HeatingDesuperheater) {
int suppHeatingCoilIndex;
HeatingCoils::GetCoilIndex(state, thisFurnace.SuppHeatCoilName, suppHeatingCoilIndex, errorFound);
if (suppHeatingCoilIndex > 0) {
suppHeatingCoilRTF = state.dataHeatingCoils->HeatingCoil(suppHeatingCoilIndex).RTF;
}
}
if (errorFound) {
ShowSevereError(state, format("The index of \"{}\" is not found", thisFurnace.SuppHeatCoilName));
ShowContinueError(state, format("...occurs for {}", thisFurnace.Name));
errorFound = false;
}
state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopHeatingCoilMaxRTF =
max(refAFNLoopHeatingCoilMaxRTF, heatingCoilRTF, suppHeatingCoilRTF);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set the maximum RTF at the furnace level, overwriting the one calculated at the heating coil level (==1).

Comment on lines +1923 to +1927
// Check that the runtime fraction is less than one so the impact of cycling fan is correctly accounted for in the AFN
EXPECT_TRUE(state->dataAirLoop->AirLoopAFNInfo(1).AFNLoopHeatingCoilMaxRTF < 1);

// Check that the system heating capacity is the same as the heating coil
EXPECT_NEAR(state->dataHeatingCoils->HeatingCoil(1).NominalCapacity, state->dataFurnaces->Furnace(1).DesignHeatingCapacity, 1e-6);
Copy link
Collaborator Author

@lymereJ lymereJ Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New unit test that checks for both fixes. develop fails both assertions.

@github-actions
Copy link

⚠️ Regressions detected on ubuntu-24.04 for commit 107359c

Regression Summary
  • Audit: 35
  • RDD: 33
  • EIO: 1
  • ESO Big Diffs: 1
  • MTR Big Diffs: 1
  • Table Big Diffs: 1
  • Table String Diffs: 1

@lymereJ
Copy link
Collaborator Author

lymereJ commented Dec 12, 2025

Here's a plot of the conditioned zone infiltration sensible heat loss: in blue is shown the case with no leakage and in maroon the original case with leakage, and in orange is the case with leakage but from this branch. As expected, we do see still difference with the case with no leakage, but much less.

image

Simulation results of the defect case now show that the case with increased leakage uses more heating energy than the case with no leakage.

Big diffs are only shown for AirflowNetwork_MultiZone_House_OvercoolDehumid.idf which has the same issue as the defect file, thus the diffs are expected.

@lymereJ lymereJ marked this pull request as ready for review December 12, 2025 19:22
@github-actions
Copy link

⚠️ Regressions detected on macos-14 for commit 107359c

Regression Summary
  • Audit: 35
  • RDD: 33
  • EIO: 1
  • ESO Big Diffs: 1
  • MTR Big Diffs: 1
  • Table Big Diffs: 1
  • Table String Diffs: 1

@lymereJ lymereJ added the AirflowNetwork Related primarily on airflow-network portions of the codebase label Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AirflowNetwork Related primarily on airflow-network portions of the codebase Defect Includes code to repair a defect in EnergyPlus OutputChange Code changes output in such a way that it cannot be merged after IO freeze

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Increased duct leakage result in decrease in heating energy use in cold climate

3 participants