Skip to content

Average mixer inlets to avoid diffs from mixer branch order - #11730

Open
rraustad wants to merge 8 commits into
developfrom
6508-Splitter-Mixer-branch-order-causes-diffs
Open

rraustad wants to merge 8 commits into
developfrom
6508-Splitter-Mixer-branch-order-causes-diffs

Conversation

@rraustad

@rraustad rraustad commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

Pull request overview

Description of the purpose of this PR

User reported diffs when the only change in an input file was the order of mixer/splitter branch order. When flow rate is 0, instead of choosing the first inlet branch conditions as the outlet branch conditions, average the inlet branch conditions to determine the output branch condition. If using the first branch is arbitrary, then averaging the branches is also a reasonable outcome.

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 any defect files are updated to a more recent version, upload new versions here or on DevSupport
  • If IDD requires transition, transition source, rules, ExpandObjects, and IDFs must be updated, and add IDDChange label
  • If structural output changes, add to output rules file and add OutputChange label
  • If adding/removing any LaTeX docs or figures, update that document's CMakeLists file dependencies
  • If adding/removing any output files (e.g., eplustbl.*)
    • Update ..\scripts\Epl-run.bat
    • Update ..\scripts\RunEPlus.bat
    • Update ..\src\EPLaunch\ MainModule.bas, epl-ui.frm, and epl.vbp (VersionComments)
    • Update ...github\workflows\energyplus.py

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

@rraustad rraustad self-assigned this Aug 7, 2026
@rraustad rraustad added the Defect Includes code to repair a defect in EnergyPlus label Aug 7, 2026
@rraustad

rraustad commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

@mitchute I am testing this change for diffs. It may not fix the issue entirely but it seems to be a reasonable alternative for the previous assumption.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit dcd2547

Regression Summary
  • EIO: 110
  • ESO Big Diffs: 77
  • ESO Small Diffs: 55
  • ERR: 9
  • Table Big Diffs: 18
  • Table String Diffs: 9
  • MTR Small Diffs: 96
  • MTR Big Diffs: 7
  • Table Small Diffs: 76
  • EDD: 1

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit dcd2547

Regression Summary
  • EIO: 111
  • ESO Big Diffs: 77
  • ESO Small Diffs: 56
  • ERR: 9
  • Table Big Diffs: 17
  • Table String Diffs: 9
  • Table Small Diffs: 77
  • MTR Small Diffs: 97
  • MTR Big Diffs: 7
  • EDD: 1

mixer.OutletPressure += mixer.InletPressure(InletNodeNum) / mixer.NumInletNodes;
mixer.OutletEnthalpy += mixer.InletEnthalpy(InletNodeNum) / mixer.NumInletNodes;
mixer.OutletTemp += mixer.InletTemp(InletNodeNum) / mixer.NumInletNodes;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If choice of the first branch on the mixer to pass conditions to the outlet when mass flow = 0 is arbitrary, and prone to cause diffs when branch order is switched, then using the average conditions of all branches is a better "arbitrary" choice and less prone to branch order diffs.

@mitchute

Copy link
Copy Markdown
Collaborator

Thanks @rraustad. I added CO2 and GenContam to this as well, since they also seem to fall under the same no-flow condition: a1120eb

I expect we can approve/merge this one as soon as test come back.

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on macos-14 for commit a5fff87

Regression Summary
  • ESO Small Diffs: 80
  • EIO: 111
  • ESO Big Diffs: 77
  • ERR: 9
  • Table Big Diffs: 18
  • Table String Diffs: 10
  • MTR Small Diffs: 98
  • MTR Big Diffs: 7
  • Table Small Diffs: 77
  • EDD: 1

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit a5fff87

Regression Summary
  • ESO Small Diffs: 81
  • EIO: 112
  • ESO Big Diffs: 77
  • ERR: 9
  • Table Big Diffs: 18
  • Table String Diffs: 10
  • Table Small Diffs: 77
  • MTR Small Diffs: 99
  • MTR Big Diffs: 7
  • EDD: 1

@mitchute

Copy link
Copy Markdown
Collaborator

@rraustad, the fix here is "working," in the sense that it removes the dependence on mixer inlet order. Under no-flow conditions, the mixer outlet is now set to the average of the mixer inlet states rather than simply forwarding the state of the first inlet.

The table below was generated by running Standard621SimplifiedProcedure_PIUterminal.idf with detailed reporting enabled for the mixer inlet and outlet nodes.

Time Mixer Feature inlet states Branch outlet Baseline inlet states Baseline outlet Outlet difference (feature − baseline)
01/01 00:31:04 Zone 1 PIU I1: 12.384 / 0.0026584 / 19153.9; I2: 14.842 / 0.0025974 / 21481.9 13.613 / 0.0026279 / 20317.9 I1: 14.842 / 0.0025974 / 21481.9; I2: 14.842 / 0.0025974 / 21481.9 14.842 / 0.0025974 / 21481.9 ΔT −1.229°C; ΔW +0.0000305; Δh −1164.0 J/kg
01/01 00:31:04 Zone 3 PIU I1: 12.384 / 0.0026584 / 19153.9; I2: 14.873 / 0.0024804 / 21216.9 13.628 / 0.0025694 / 20185.4 I1: 14.842 / 0.0025974 / 21481.9; I2: 14.873 / 0.0024804 / 21216.9 14.842 / 0.0025974 / 21481.9 ΔT −1.214°C; ΔW −0.0000280; Δh −1296.5 J/kg
01/01 00:31:04 Return mixer I1: 14.842 / 0.0025974 / 21481.9; I2: 7.441 / 0.0028974 / 14763.0; I3: 14.873 / 0.0024804 / 21216.9 12.384 / 0.0026584 / 19153.9 Same as feature 14.842 / 0.0025974 / 21481.9 ΔT −2.458°C; ΔW +0.0000610; Δh −2328.0 J/kg
01/01 00:32:08 Zone 1 PIU I1: 12.053 / 0.0026479 / 18792.5; I2: 14.399 / 0.0025854 / 21003.9 13.226 / 0.0026167 / 19898.2 I1: 14.399 / 0.0025854 / 21003.9; I2: 14.399 / 0.0025854 / 21003.9 14.399 / 0.0025854 / 21003.9 ΔT −1.173°C; ΔW +0.0000313; Δh −1105.7 J/kg
01/01 00:32:08 Zone 3 PIU I1: 12.053 / 0.0026479 / 18792.5; I2: 14.344 / 0.0024683 / 20652.4 13.198 / 0.0025581 / 19722.4 I1: 14.399 / 0.0025854 / 21003.9; I2: 14.344 / 0.0024683 / 20652.4 14.399 / 0.0025854 / 21003.9 ΔT −1.201°C; ΔW −0.0000273; Δh −1281.5 J/kg
01/01 00:32:08 Return mixer I1: 14.399 / 0.0025854 / 21003.9; I2: 7.417 / 0.0028901 / 14721.2; I3: 14.344 / 0.0024683 / 20652.4 12.053 / 0.0026479 / 18792.5 Same as feature 14.399 / 0.0025854 / 21003.9 ΔT −2.347°C; ΔW +0.0000625; Δh −2211.4 J/kg

However, looking at the regression results, this change is producing significant differences in energy use and system operation for some files. For example, CompSetPtControl.idf shows the following and has about a 5% difference in site energy.

Screenshot from 2026-08-11 14-35-31

Conceptually, when there is no flow through the mixer, the outlet state doesn't (shouldn't?) have meaning. By propagating or averaging those values, we are assigning that information anyway, and it's obviously getting consumed somewhere downstream.

I would like to think through this a bit more before merging.

@mitchute

mitchute commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

@rraustad to try to move this one forward, I spent some time this afternoon thinking about it. Instead of averaging the inlet node conditions when flow is zero, another option might be to latch the zero-flow mixer outlet conditions to the previous outlet conditions when flow was non-zero. 4746dae was my attempt. I'm not sure it's any better from a results perspective, but maybe (in my mind at least) it's a slightly more palatable explanation of how to do this. We can revert it if you or others would rather stick with the inlet averaging approach.

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on macos-15 for commit bb1af83

Regression Summary
  • ESO Big Diffs: 161
  • EIO: 195
  • MTR Small Diffs: 133
  • Table Small Diffs: 99
  • Table String Diffs: 30
  • ERR: 26
  • ESO Small Diffs: 62
  • Table Big Diffs: 52
  • MTR Big Diffs: 32
  • EDD: 3
  • PERF_LOG: 2
  • JSON Big Diffs: 1

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit bb1af83

Regression Summary
  • ESO Big Diffs: 161
  • EIO: 192
  • MTR Small Diffs: 133
  • Table Small Diffs: 94
  • Table String Diffs: 30
  • Table Big Diffs: 56
  • ESO Small Diffs: 62
  • ERR: 26
  • MTR Big Diffs: 32
  • EDD: 3
  • PERF_LOG: 2
  • JSON Big Diffs: 1

@rraustad

rraustad commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

This branch, as it stands now, compared to develop using CompSetPtControl is not as far off as found before. Maybe was compared to a develop branch that was behind this branch? Only 6 W difference now. Annual Site Energy nearly matches at 200.34 vs 200.31 GJ. For the previous figure, it did not make sense because the big diff came in the middle of the summer day when the air system and any mixers were operating at flow > 0, so there's that. What is also unexplained is why does the condenser cooling energy jump like that now at mid-day, when things should be relatively flat as seen by the chilled water loop demand? Note that this is not even a fair comparison as I did not pull develop into this branch before I compared to latest develop.

image image

@rraustad

rraustad commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Investigation of what magnitude should be expected from the CompSetPtControl file plant demand plots. It appears to me that the condenser demand rate magnitude (blue dotted) at the end of chiller operation is higher than expected. The chiller plant heat rate is ~31k and jumps to 35k watts while the condenser heat rate is ~40k to 70k watts. Both chiller heat rates summed (red dotted) show a big magnitude difference while only the plant condenser report (blue dotted) shows this on the condenser side. Could be pump heat? I'm not sure. The chilled water pump is CS while the condenser pump is VS, this might actually explain this but I did not look into pump heat in this pass.

image

UPDATE: pump heat is less than 1 KW so that does not explain this.

image

@mitchute

Copy link
Copy Markdown
Collaborator

@rraustad I think you had this right the first time. I'll post a more detailed review of results later, but I think inlet averaging is the right approach (rather than latching to the past timestep). That, at least, will not affect single-inlet system results.

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit 3b18e38

Regression Summary
  • EIO: 111
  • ESO Big Diffs: 77
  • ESO Small Diffs: 56
  • ERR: 9
  • Table Big Diffs: 18
  • Table String Diffs: 9
  • MTR Small Diffs: 97
  • MTR Big Diffs: 7
  • Table Small Diffs: 76
  • EDD: 1

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on macos-15 for commit 3b18e38

Regression Summary
  • EIO: 110
  • ESO Big Diffs: 77
  • ESO Small Diffs: 55
  • ERR: 9
  • Table Big Diffs: 18
  • Table String Diffs: 9
  • MTR Small Diffs: 96
  • MTR Big Diffs: 7
  • Table Small Diffs: 76
  • EDD: 1

@mitchute

Copy link
Copy Markdown
Collaborator

Annual regression results

Summary: The branch's effect on annual energy is small. Of 825 files, 173 have big diffs, and 162 of those change total site energy by less than 0.01%. Only CompSetPtControl (−0.34%) and HybridVentilationControl (−0.10%) change by more than 0.1%, and no file changes by 0.5% or more. Every file with big diffs contains an AirLoopHVAC:ZoneMixer, so no diffs appear where the mixer isn't used.

The diffs come from the change to zero-flow mixer outlet conditions. Develop takes them from the last inlet in the mixer's list, so the result depends on inlet order. The branch averages the inlets using the last flow fractions. These outlet conditions are the airloop state when equipment turns on, so they shift chiller staging, hybrid ventilation switching and morning start in a few hours, with a small effect over the year.

Run status

Item Result
Files 825
Ran successfully (develop / branch) 817 / 817
Files with big diffs 173 (99 math and table, 47 math only, 27 table only)
Big-diff files without an AirLoopHVAC:ZoneMixer 0

Total site energy change (Annual Building Performance Summary)

Change Files
Under 0.01% 162
0.01% to under 0.1% 5
0.1% to under 0.5% 2
0.5% or more 0
No ABUPS table 4

Files with a site energy change above 0.01%

File Site energy, develop → branch (GJ) Change Notes
CompSetPtControl 201.44 → 200.76 −0.34% Chiller staging differs in some hours. Changes are in April–September only.
HybridVentilationControl 389.18 → 388.79 −0.10% Gas heating 233.1 → 232.8 GJ. Occupied heating unmet hours 165.83 → 167.00.
HeatRecoveryElectricChiller 203.64 → 203.76 +0.06% Chiller heat recovery 1.55 → 1.48 GJ, so water-heater electricity increases. Most of the change is in April (+129 MJ).
HybridVentilationControlGlobalSimple 351.89 → 351.77 −0.03% Cooling electricity 18.69 → 18.63 GJ. Occupied heating unmet hours 34.83 → 35.50.
_HybridVentilationControl_MinTime 390.60 → 390.49 −0.03% Gas heating 233.77 → 233.66 GJ.
5ZoneVAV-ChilledWaterStorage-Mixed 566.71 → 566.60 −0.02% A run of max-iteration warnings moves from 08/03 to 08/11.
MultiSpeedACFurnace 267.82 → 267.86 +0.02% Occupied heating unmet hours 293.67 → 294.83.

The largest change in any other file is 0.005%.

Unmet hours: 25 facility-level changes in occupied heating or cooling unmet hours, none larger than 1.34 h (DXCoilSystemAuto and UnitarySystem_DXCoilSystemAuto: 648.83 → 650.17 h cooling).

Warnings: Most changes are a few messages either way. The larger ones:

  • VRFMultispeedFan: about 33,000 fewer HVAC max-iteration warnings (251,302 → 217,849). Energy changes by only −0.17 MJ.
  • EMSAirflowNetworkOpeningControlByHumidity and PythonPluginAirflowNetworkOpeningControlByHumidity: 868 more "window or door open during HVAC operation" warnings each.
  • FreeCoolingChiller: 10 more detailed-geometry cooling coil warnings.

_FanCoilHybridVentAFN (test-only file): warmup convergence

On the branch, RUN PERIOD 2 (07/07–07/11) needs 25 warmup days instead of 8 and ends with three CheckWarmupConvergence severe errors. This comes from how sensitive the file is, not from a defect in the branch:

Case Warmup days Warmup severe errors
Develop, inlet order as in the IDF 8 0
Develop, inlet order swapped 25 (4-day cycle) 3
Branch, inlet order as in the IDF 25 (2-day cycle) 3
Branch, inlet order swapped 25 (output identical to the IDF order) 3

From 03:30 to 06:40, the West zone hybrid ventilation controller switches between natural ventilation and the fan coil within single timesteps. That controller has no air loop, and the West zone isn't connected to the mixer. Small changes on the air-loop side are enough to keep warmup from settling. Develop converges only with the inlet order in the IDF, which is the order dependence from #6508 that this PR removes. The branch gives the same result for either order.

@rraustad this is as far as I plan to go on this one. I'm comfortable with the inlet averaging strategy, but it does result in a lot of diffs, which at this point I believe are all explained by the particulars of each file and how sensitive their specific controls are.

@mitchute mitchute left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@rraustad please take a look here, when you get a chance.


auto &mixer = state.dataMixerComponent->MixerCond(MixerNum);

if (state.dataGlobal->BeginEnvrnFlag && mixer.InletFlowFracResetAtBeginEnv) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Resetting the averaging at the beginning of each environment.

Comment on lines +499 to +501
for (InletNodeNum = 1; InletNodeNum <= mixer.NumInletNodes; ++InletNodeNum) {
mixer.InletFlowFraction(InletNodeNum) = mixer.InletMassFlowRate(InletNodeNum) / mixer.OutletMassFlowRate;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Weight the outlet based each inlet branch's fraction of total flow.

Comment on lines +543 to +555
// Mass flow in air loop is zero and loop is not operating.
// Use the last active inlet flow fractions with the current inlet conditions. Before flow history exists,
// the fractions are equal (set at the start of each environment).
mixer.OutletHumRat = 0.0;
mixer.OutletPressure = 0.0;
mixer.OutletEnthalpy = 0.0;
for (InletNodeNum = 1; InletNodeNum <= mixer.NumInletNodes; ++InletNodeNum) {
mixer.OutletHumRat += mixer.InletFlowFraction(InletNodeNum) * mixer.InletHumRat(InletNodeNum);
mixer.OutletPressure += mixer.InletFlowFraction(InletNodeNum) * mixer.InletPressure(InletNodeNum);
mixer.OutletEnthalpy += mixer.InletFlowFraction(InletNodeNum) * mixer.InletEnthalpy(InletNodeNum);
}
// A single inlet passes its temperature through so the outlet matches the inlet exactly
mixer.OutletTemp = (mixer.NumInletNodes == 1) ? mixer.InletTemp(1) : PsyTdbFnHW(mixer.OutletEnthalpy, mixer.OutletHumRat);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Average the inlet conditions here.

Comment on lines +605 to +608
outletNode.CO2 = 0.0;
for (InletNodeNum = 1; InletNodeNum <= mixer.NumInletNodes; ++InletNodeNum) {
outletNode.CO2 += mixer.InletFlowFraction(InletNodeNum) * state.dataLoopNodes->Node(mixer.InletNode(InletNodeNum)).CO2;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Include CO2 and generic contam here, too.

@mitchute mitchute added the Staged By adding the "Staged" label, some expensive checks needed before merge are run. label Oct 29, 2026
@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on ubuntu-24.04 for commit 496cb9b

Regression Summary
  • EIO: 110
  • ESO Big Diffs: 77
  • ESO Small Diffs: 54
  • ERR: 8
  • Table Small Diffs: 78
  • Table String Diffs: 12
  • Table Big Diffs: 15
  • MTR Small Diffs: 93
  • MTR Big Diffs: 8
  • EDD: 1

@github-actions

Copy link
Copy Markdown

⚠️ Regressions detected on macos-15 for commit 496cb9b

Regression Summary
  • EIO: 110
  • ESO Big Diffs: 77
  • ESO Small Diffs: 53
  • Table Big Diffs: 19
  • ERR: 8
  • Table String Diffs: 9
  • MTR Small Diffs: 92
  • MTR Big Diffs: 8
  • Table Small Diffs: 75
  • EDD: 1

This branch has not been deployed

No deployments
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 Staged By adding the "Staged" label, some expensive checks needed before merge are run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Order of zones on splitter/mixer causing diffs

4 participants