Fix #8904: OA node wetbulb actuator silently fails via Python plugin API#11540
Merged
Fix #8904: OA node wetbulb actuator silently fails via Python plugin API#11540
Conversation
SetOANodeValues gates HumRat recalculation on IsLocalNode, which is not set for Python plugin actuators. Widen gate to also check EMSOverrideOutAirDryBulb/WetBulb so the psychrometric recalc runs whenever an override is actually active, regardless of IsLocalNode. Add unit test reproducing the bug with IsLocalNode=false.
Python plugin path (getActuatorHandle) never set IsLocalNode=true, unlike traditional EMS path in EMSManager::SetupNodeSetPointsAsActuators. This also fixes AirflowNetwork propagation for plugin OA overrides.
mitchute
reviewed
May 4, 2026
Comment on lines
+460
to
+468
| // Mirror EMSManager::SetupNodeSetPointsAsActuators for Python plugin path (issue #8904) | ||
| if (typeUC == "OUTDOOR AIR SYSTEM NODE") { | ||
| for (int NodeNum = 1; NodeNum <= thisState->dataLoopNodes->NumOfNodes; ++NodeNum) { | ||
| if (EnergyPlus::Util::makeUPPER(thisState->dataLoopNodes->NodeID(NodeNum)) == keyUC) { | ||
| thisState->dataLoopNodes->Node(NodeNum).IsLocalNode = true; | ||
| break; | ||
| } | ||
| } | ||
| } |
Collaborator
There was a problem hiding this comment.
This seems like the wrong place for this. Why is it here?
Contributor
Author
There was a problem hiding this comment.
Good catch. This block doesn't belong here and it's redundant.
mitchute
reviewed
May 4, 2026
Comment on lines
+595
to
+596
| if (state.dataLoopNodes->Node(NodeNum).IsLocalNode || state.dataLoopNodes->Node(NodeNum).EMSOverrideOutAirDryBulb || | ||
| state.dataLoopNodes->Node(NodeNum).EMSOverrideOutAirWetBulb) { |
Collaborator
There was a problem hiding this comment.
Yep, it looks like this is the critical fix here. I brought the new unit test over to develop and it fails, but passes on this branch without the datatransfer.cc changes above.
mitchute
approved these changes
May 5, 2026
Collaborator
mitchute
left a comment
There was a problem hiding this comment.
This is ready. Merging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #8904 — EMS "Outdoor Air System Node" / "Wetbulb Temperature" actuator has no effect when set via the Python plugin API (
getActuatorHandle). Drybulb works; wetbulb is silently discarded.Scope: Python plugin API path only. Traditional EMS wetbulb overrides work correctly (fixed in PR #6841, which sets
IsLocalNode=trueduringSetupNodeSetPointsAsActuatorsatEMSManager.cc:1427). The issue's follow-up EMS example appears broken due to a typo (SET cond_dbt = 23.9should beSET cond_wbt = 23.9) — Node 1's reported Twb=19.41 confirms the traditional path works.Root cause
SetOANodeValues(OutAirNodeManager.cc:595) gates thePsyWFnTdbTwbPbHumRat recalculation behindif (Node.IsLocalNode). Traditional EMS setsIsLocalNode = trueduring setup viaSetupNodeSetPointsAsActuators(EMSManager.cc:1427), butgetActuatorHandle()(datatransfer.cc:457) never does — by the time a Python plugin requests the handle, setup has already run and found no match.Fix
Two complementary changes:
OutAirNodeManager.cc:595IsLocalNode || EMSOverrideOutAirDryBulb || EMSOverrideOutAirWetBulb— EMS override bools are ground truth set by both traditional and plugin pathsdatatransfer.cc:461-468IsLocalNode = trueingetActuatorHandlewhen type is"OUTDOOR AIR SYSTEM NODE", mirroringEMSManager.cc:1427— covers AirflowNetwork and any otherIsLocalNodeconsumerOutAirNodeManager.unit.ccFlow tree
Test added
OutAirNodeManager_EMSWetbulbOverride_NoIsLocalNode— two OA nodes withIsLocalNode=falseand EMS wetbulb overrides active. Verifies HumRat is recalculated from overridden temps, not left at environment value.Test plan