Skip to content

Fix #11039: supp heat blocked by negative QToHeatSetPt#11546

Open
brianlball wants to merge 7 commits intodevelopfrom
issue-11039-supp-heat-blocked
Open

Fix #11039: supp heat blocked by negative QToHeatSetPt#11546
brianlball wants to merge 7 commits intodevelopfrom
issue-11039-supp-heat-blocked

Conversation

@brianlball
Copy link
Copy Markdown
Contributor

@brianlball brianlball commented Apr 25, 2026

Summary

  • Supplemental heating was blocked when zone predictor overestimates zone temp, driving QToHeatSetPt negative. The supp check (FullSensibleOutput < QToHeatSetPt) always passes when QToHeatSetPt is negative.
  • For constant-fan systems, initLoadBasedControl overwrites ZoneLoad = QToHeatSetPt, making the primary coil target the wrong (negative) load. The supp check then sees the primary "met" the target and doesn't fire.
  • Fix: (1) cap ZoneLoad at max(QToHeatSetPt, QZnReq) in 3 constant-fan heating paths to preserve original demand signal; (2) floor suppLoadTarget at 0 when HeatingLoad is true, so supp fires when primary can't achieve neutral output.

Test plan

  • Unit test SuppHeatBlocked_NegativeQToHeatSetPt — cycling fan, SingleHeat, negative QToHeatSetPt. Fails on develop, passes with fix.
  • Unit test SuppHeatBlocked_ConstantFan_DualHeatCool — constant fan, DualHeatCool deadband, negative QToHeatSetPt. Fails on develop, passes with fix.
  • All 63 UnitarySystem unit tests pass
  • Integration test: VSHeatPumpWaterToAirEquationFit.idf unmet heating hours 53.25 → 15.00 (-72%)
  • 5ZoneWaterLoopHeatPump: 0 unmet hours baseline and fix (no regression)
  • pre-commit and gcc warning scan clean

Unit test proves supplemental heating is blocked when QToHeatSetPt < 0
despite zone demand exceeding primary coil capacity. The check at
controlUnitarySystemtoLoad (~line 8125) compares FullSensibleOutput
against QToHeatSetPt — when the zone predictor underestimates heating
need, QToHeatSetPt goes negative and backup never fires.

Test will pass once the fix is applied.
Supp check used QToHeatSetPt as threshold — when negative (predictor
overestimates zone temp), positive coil output always passes check,
blocking supp. Two fixes:

1. Constant-fan ZoneLoad overwrite (lines 10808/10830/10856/10878):
   use max(QToHeatSetPt, QZnReq) to preserve original demand signal.
   Previously missed the DualHeatCool deadband case at line 10878.

2. Supp check zero-floor: when HeatingLoad is true but suppLoadTarget
   is negative, floor at 0 so supp fires when primary can't achieve
   neutral output. Handles SingleHeat where QZnReq==QToHeatSetPt.

VSHeatPump integration test: 53.25 → 15.00 unmet heating hours (-72%).
New test: SuppHeatBlocked_ConstantFan_DualHeatCool — continuous fan,
DualHeatCool deadband, QToHeatSetPt=-500, QToCoolSetPt=3000. Verifies
supp fires via the zero-floor fix when primary targets negative load.
Fails on unfixed code, passes with fix.

Reverted line 10878 (DualHeatCool deadband) from max(QToHeatSetPt,QZnReq)
back to QToHeatSetPt. The max() produced ZoneLoad=0 which triggered the
small-load guard at line 10942, killing HeatingLoad. The supp check
zero-floor handles this case instead.
@brianlball brianlball added Defect Includes code to repair a defect in EnergyPlus DoNotMerge Code that requires additional attention and investigation and removed Defect Includes code to repair a defect in EnergyPlus labels Apr 25, 2026
@brianlball brianlball marked this pull request as draft April 25, 2026 01:45
@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit 1d837c0

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit 1d837c0

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@rraustad
Copy link
Copy Markdown
Collaborator

Is there a defect file to test? I won't believe this change unless I see a defect file's result change for the better.

@mitchute mitchute added DoNotPublish Includes changes that shouldn't be reported in the changelog Defect Includes code to repair a defect in EnergyPlus and removed DoNotPublish Includes changes that shouldn't be reported in the changelog labels Apr 25, 2026
@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit 1d837c0

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit 1d837c0

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@brianlball brianlball marked this pull request as ready for review April 25, 2026 13:02
@brianlball
Copy link
Copy Markdown
Contributor Author

brianlball commented Apr 25, 2026

Is there a defect file to test? I won't believe this change unless I see a defect file's result change for the better.

@rraustad There is in the issue #11039

Would certainly appreciate your thoughts on this issue.
I'm also not certain that this is the right fix. WIP

max(QToHeatSetPt, QZnReq) produced ZoneLoad=0 in deadband,
triggering the small-load guard that cleared HeatingLoad.
Supp heat zero-floor at line 8207 is sufficient alone.
@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit efe4f37

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit efe4f37

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit c2a02c8

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@github-actions
Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit c2a02c8

Regression Summary
  • EIO: 8
  • EDD: 1
  • ESO Big Diffs: 7
  • Table Big Diffs: 8
  • MTR Small Diffs: 5
  • ESO Small Diffs: 1
  • Table Small Diffs: 1
  • ERR: 2
  • MTR Big Diffs: 2

@brianlball brianlball removed the Defect Includes code to repair a defect in EnergyPlus label Apr 25, 2026
@rraustad
Copy link
Copy Markdown
Collaborator

rraustad commented Apr 27, 2026

At 14:00 on Jan 1, why is speed 2 out of 4 on and the zone temperature is below the heating set point? The supp coil is on here but there is more capacity available from the main heating coil. It seems like there is something wrong with the multispeed coil and onlhy possibly the logic for the supp coil.

image

This branch shows the heating coil and supp heater are on when they should not be. The zone temp is forced to the cooling set point when the heating coils should not be on when the zone temp is above the heating set point temperature.

image

@brianlball
Copy link
Copy Markdown
Contributor Author

brianlball commented Apr 27, 2026

@rraustad Thanks for your comment in the issue. I had just assumed the coil was maxed out and why the supp heat was needed. Thats clearly not the case and my issue diagnosis was wrong. Something else is going on.

I'm still new to this part of the code base and trying to figure things out, but I think QToHeatSetPt is not being set correctly and/or not updated.

@rraustad
Copy link
Copy Markdown
Collaborator

rraustad commented Apr 29, 2026

Here's a figure I've been working on to help explain the difference between QToHeat/CoolSetPt and ZoneLoad. ZoneLoad starts out as the raw QToHeat/CoolSetPt load (comes from RemainingOutputRequired or SequencedOutputRequired from predictor) but can switch signs if there is continuous fan operation and depends on the load imposed by outdoor air. If there is a HeatingLoad then ZoneLoad can be positive or negative, depending on the outdoor air conditions when using constant fan. The red text is what happens when OA causes the load direction to change. If there is a negative ZoneLoad when HeatingLoad = true, that usually means the zone temp will wind up at the heating SP temperature (similar logic for cooling). In this case the system is actually providing cooling to the zone but the heating coil (or supp heater) is on, not the cooling coil. The system is just not providing excess cooling to push the zone temperature below the heating set point temperature. The 2 Q loads shown in (xxx) at the right of the figure just show that it's possible to have either sign for these Q variables based on what predictor calculates. Black is no system operating and red is system operating with no coils active and only the fan is operating. Coils will be activated later after ZoneLoad is determined and HeatLoad/CoolLoad are set. This happens in initLoadBasedControl inside this conditional:

    // System load calculation for constant fan systems
    if (this->m_FanOpMode == HVAC::FanOp::Continuous) {
image

@mitchute mitchute added the Defect Includes code to repair a defect in EnergyPlus label May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Defect Includes code to repair a defect in EnergyPlus DoNotMerge Code that requires additional attention and investigation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants