Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/EnergyPlus/DataRuntimeLanguage.hh
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ namespace DataRuntimeLanguage {
bool CheckedOkay; // set to true once matched to available actuator
int ErlVariableNum; // points to global Erl variable, matches Name
int ActuatorVariableNum; // points to index match in EMSActuatorAvailable structure
bool wasActuated = false; // issue #10944: true once any Erl program has set this actuator
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adding a flag here seems quite reasonable.


// Default Constructor
ActuatorUsedType() : CheckedOkay(false), ErlVariableNum(0), ActuatorVariableNum(0)
Expand Down
7 changes: 4 additions & 3 deletions src/EnergyPlus/EMSManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ namespace EMSManager {
state.dataRuntimeLang->NumExternalInterfaceFunctionalMockupUnitImportActuatorsUsed +
state.dataRuntimeLang->NumExternalInterfaceFunctionalMockupUnitExportActuatorsUsed;
++ActuatorUsedLoop) {
auto const &thisActuatorUsed = state.dataRuntimeLang->EMSActuatorUsed(ActuatorUsedLoop);
auto &thisActuatorUsed = state.dataRuntimeLang->EMSActuatorUsed(ActuatorUsedLoop);

int ErlVariableNum = thisActuatorUsed.ErlVariableNum;
if (ErlVariableNum <= 0) {
Expand All @@ -343,6 +343,7 @@ namespace EMSManager {
if (thisErlVar.Value.Type == DataRuntimeLanguage::Value::Null) {
*thisActuatorAvail.Actuated = false;
} else {
thisActuatorUsed.wasActuated = true; // issue #10944
// Set the value and the actuated flag remotely on the actuated object via the pointer
switch (thisActuatorAvail.PntrVarTypeUsed) {
case DataRuntimeLanguage::PtrDataType::Real: {
Expand Down Expand Up @@ -1983,9 +1984,9 @@ namespace EMSManager {
void checkForUnusedActuatorsAtEnd(EnergyPlusData &state)
{
// call at end of simulation to check if any of the user's actuators were never initialized.
// Could be a mistake we want to help users catch // Issue #4404.
// Could be a mistake we want to help users catch // Issues #4404, #10944.
for (int actuatorUsedLoop = 1; actuatorUsedLoop <= state.dataRuntimeLang->numActuatorsUsed; ++actuatorUsedLoop) {
if (!state.dataRuntimeLang->ErlVariable(state.dataRuntimeLang->EMSActuatorUsed(actuatorUsedLoop).ErlVariableNum).Value.initialized) {
if (!state.dataRuntimeLang->EMSActuatorUsed(actuatorUsedLoop).wasActuated) {
ShowWarningError(state,
"checkForUnusedActuatorsAtEnd: Unused EMS Actuator detected, suggesting possible unintended programming error or "
"spelling mistake.");
Expand Down
47 changes: 47 additions & 0 deletions tst/EnergyPlus/unit/EMSManager.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3019,3 +3019,50 @@ TEST_F(EnergyPlusFixture, EMSManager_Sensor_On_ScheduleConstant)
EXPECT_FALSE(schedDirect->EMSActuatedOn);
EXPECT_EQ(0.0, schedDirect->EMSVal);
}

TEST_F(EnergyPlusFixture, UnusedActuatorWarning)
{
// Issue #10944: user declares actuator A1, typos it as Al in the program.
// Al becomes a fresh local Erl variable; A1 is never assigned.
// End-of-sim check must warn about unused actuator A1.
std::string const idf_objects = delimited_string({

"OutdoorAir:Node, Test node;",

"EnergyManagementSystem:Actuator,",
"A1, !- Name",
"Test node, !- Actuated Component Unique Name",
"System Node Setpoint, !- Actuated Component Type",
"Temperature Setpoint; !- Actuated Component Control Type",

"EnergyManagementSystem:ProgramCallingManager,",
"Typo Test Manager, !- Name",
"BeginTimestepBeforePredictor, !- EnergyPlus Model Calling Point",
"TypoProgram; !- Program Name 1",

"EnergyManagementSystem:Program,",
"TypoProgram,",
"Set Al = 2;",

});

ASSERT_TRUE(process_idf(idf_objects));
state->init_state(*state);

OutAirNodeManager::SetOutAirNodes(*state);

EMSManager::CheckIfAnyEMS(*state);

state->dataEMSMgr->FinishProcessingUserInput = true;

bool anyRan;
EMSManager::ManageEMS(*state, EMSManager::EMSCallFrom::SetupSimulation, anyRan, ObjexxFCL::Optional_int_const());
EMSManager::ManageEMS(*state, EMSManager::EMSCallFrom::BeginTimestepBeforePredictor, anyRan, ObjexxFCL::Optional_int_const());

compare_err_stream(""); // drop any setup chatter
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good unit test. I'm not sure this call to compare_err_stream is needed since you are properly using the compare_err_stream_substring function below. The err stream does not need to be cleared of any chatter. It's not harming anything, but not necessary in the future.


EMSManager::checkForUnusedActuatorsAtEnd(*state);

EXPECT_TRUE(compare_err_stream_substring("Unused EMS Actuator detected", false));
EXPECT_TRUE(compare_err_stream_substring("A1", true));
}
Loading