From 1272641cae472856fdfddaf4c1809b8d48e909dc Mon Sep 17 00:00:00 2001 From: amirroth Date: Sun, 8 Dec 2024 11:34:03 -0500 Subject: [PATCH] Fix EMS issues and several others --- src/EnergyPlus/DataRuntimeLanguage.hh | 2 +- src/EnergyPlus/EMSManager.cc | 52 +++++----- src/EnergyPlus/PlantCentralGSHP.cc | 1 + src/EnergyPlus/ScheduleManager.cc | 96 ++++++++++--------- src/EnergyPlus/WeatherManager.cc | 2 +- .../ZoneContaminantPredictorCorrector.cc | 4 +- ...HRAE901_RetailStripmall_STD2019_Denver.idf | 2 +- tst/EnergyPlus/unit/DataHeatBalance.unit.cc | 18 ++-- 8 files changed, 91 insertions(+), 86 deletions(-) diff --git a/src/EnergyPlus/DataRuntimeLanguage.hh b/src/EnergyPlus/DataRuntimeLanguage.hh index 451d0c59e24..d93d794f900 100644 --- a/src/EnergyPlus/DataRuntimeLanguage.hh +++ b/src/EnergyPlus/DataRuntimeLanguage.hh @@ -806,7 +806,7 @@ struct RuntimeLanguageData : BaseGlobalStruct DataRuntimeLanguage::ErlValueType True = DataRuntimeLanguage::ErlValueType( DataRuntimeLanguage::Value::Null, 0.0, "", 0, 0, false, 0, "", true); // special "True" Erl variable value instance, gets reset - std::map, int> EMSActuatorAvailableMap; + std::map, int> EMSActuatorAvailableMap; void init_constant_state([[maybe_unused]] EnergyPlusData &state) override { diff --git a/src/EnergyPlus/EMSManager.cc b/src/EnergyPlus/EMSManager.cc index dc0b10fc736..9a217dae344 100644 --- a/src/EnergyPlus/EMSManager.cc +++ b/src/EnergyPlus/EMSManager.cc @@ -1958,9 +1958,9 @@ namespace EMSManager { // ScheduleManager and OutputProcessor. Followed pattern used for SetupOutputVariable void SetupEMSActuator(EnergyPlusData &state, - std::string_view cComponentTypeName, - std::string_view cUniqueIDName, - std::string_view cControlTypeName, + std::string_view objType, + std::string_view objName, + std::string_view controlTypeName, std::string_view cUnits, bool &lEMSActuated, Real64 &rValue) @@ -1981,34 +1981,32 @@ void SetupEMSActuator(EnergyPlusData &state, auto &s_lang = state.dataRuntimeLang; - std::string const objType = Util::makeUPPER(cComponentTypeName); - std::string const objName = Util::makeUPPER(cUniqueIDName); - std::string const actuatorName = Util::makeUPPER(cControlTypeName); + auto tup = std::make_tuple(std::move(Util::makeUPPER(objType)), std::move(Util::makeUPPER(objName)), std::move(Util::makeUPPER(controlTypeName))); // DataRuntimeLanguage::EMSActuatorKey const key(UpperCaseObjectType, UpperCaseObjectName, UpperCaseActuatorName); - auto found = s_lang->EMSActuatorAvailableMap.find(std::make_tuple(objType, objName, actuatorName)); - if (found == s_lang->EMSActuatorAvailableMap.end()) { - if (s_lang->numEMSActuatorsAvailable == 0) { - s_lang->EMSActuatorAvailable.allocate(s_lang->varsAvailableAllocInc); - s_lang->numEMSActuatorsAvailable = 1; - s_lang->maxEMSActuatorsAvailable = s_lang->varsAvailableAllocInc; - } else { - if (s_lang->numEMSActuatorsAvailable + 1 > s_lang->maxEMSActuatorsAvailable) { - s_lang->EMSActuatorAvailable.redimension(s_lang->maxEMSActuatorsAvailable *= 2); - } - ++s_lang->numEMSActuatorsAvailable; + if (s_lang->EMSActuatorAvailableMap.find(tup) != s_lang->EMSActuatorAvailableMap.end()) + return; + + if (s_lang->numEMSActuatorsAvailable == 0) { + s_lang->EMSActuatorAvailable.allocate(s_lang->varsAvailableAllocInc); + s_lang->numEMSActuatorsAvailable = 1; + s_lang->maxEMSActuatorsAvailable = s_lang->varsAvailableAllocInc; + } else { + if (s_lang->numEMSActuatorsAvailable + 1 > s_lang->maxEMSActuatorsAvailable) { + s_lang->EMSActuatorAvailable.redimension(s_lang->maxEMSActuatorsAvailable *= 2); } - - auto &actuator = s_lang->EMSActuatorAvailable(s_lang->numEMSActuatorsAvailable); - actuator.ComponentTypeName = cComponentTypeName; - actuator.UniqueIDName = cUniqueIDName; - actuator.ControlTypeName = cControlTypeName; - actuator.Units = cUnits; - actuator.Actuated = &lEMSActuated; // Pointer assigment - actuator.RealValue = &rValue; // Pointer assigment - actuator.PntrVarTypeUsed = DataRuntimeLanguage::PtrDataType::Real; - s_lang->EMSActuatorAvailableMap.insert_or_assign(std::make_tuple(objType, objName, actuatorName), s_lang->numEMSActuatorsAvailable); + ++s_lang->numEMSActuatorsAvailable; } + + auto &actuator = s_lang->EMSActuatorAvailable(s_lang->numEMSActuatorsAvailable); + actuator.ComponentTypeName = objType; + actuator.UniqueIDName = objName; + actuator.ControlTypeName = controlTypeName; + actuator.Units = cUnits; + actuator.Actuated = &lEMSActuated; // Pointer assigment + actuator.RealValue = &rValue; // Pointer assigment + actuator.PntrVarTypeUsed = DataRuntimeLanguage::PtrDataType::Real; + s_lang->EMSActuatorAvailableMap.insert_or_assign(std::move(tup), s_lang->numEMSActuatorsAvailable); } void SetupEMSActuator(EnergyPlusData &state, diff --git a/src/EnergyPlus/PlantCentralGSHP.cc b/src/EnergyPlus/PlantCentralGSHP.cc index 4f071ab14ae..878e2571f16 100644 --- a/src/EnergyPlus/PlantCentralGSHP.cc +++ b/src/EnergyPlus/PlantCentralGSHP.cc @@ -681,6 +681,7 @@ void GetWrapperInput(EnergyPlusData &state) state.dataPlantCentralGSHP->Wrapper(WrapperNum).AncillaryPower = state.dataIPShortCut->rNumericArgs(1); if (state.dataIPShortCut->lAlphaFieldBlanks(9)) { + state.dataPlantCentralGSHP->Wrapper(WrapperNum).ancillaryPowerSched = Sched::GetScheduleAlwaysOff(state); } else if ((state.dataPlantCentralGSHP->Wrapper(WrapperNum).ancillaryPowerSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(9))) == nullptr) { ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(9), state.dataIPShortCut->cAlphaArgs(9)); } diff --git a/src/EnergyPlus/ScheduleManager.cc b/src/EnergyPlus/ScheduleManager.cc index 6c624c65fc5..846a56f2d21 100644 --- a/src/EnergyPlus/ScheduleManager.cc +++ b/src/EnergyPlus/ScheduleManager.cc @@ -198,16 +198,15 @@ namespace Sched { auto &s_glob = state.dataGlobal; if (this->interpolation == Interpolation::Average) { for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - int begMinute = 0; - int endMinute = s_glob->MinutesInTimeStep - 1; + int begMin = 0; + int endMin = s_glob->MinutesInTimeStep - 1; for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) { this->tsVals[hr * s_glob->TimeStepsInHour + ts] = - std::accumulate(minuteVals.begin() + (hr * Constant::iMinutesInHour + begMinute), - minuteVals.begin() + (hr * Constant::iMinutesInHour + endMinute), - 0) / double(s_glob->MinutesInTimeStep); + std::accumulate(&minuteVals[hr * Constant::iMinutesInHour + begMin], &minuteVals[hr * Constant::iMinutesInHour + endMin + 1], 0) / + double(s_glob->MinutesInTimeStep); this->sumTsVals += this->tsVals[hr * s_glob->TimeStepsInHour + ts]; - begMinute = endMinute + 1; - endMinute += s_glob->MinutesInTimeStep; + begMin = endMin + 1; + endMin += s_glob->MinutesInTimeStep; } } } else { @@ -230,7 +229,7 @@ namespace Sched { sched->Name = name; sched->Num = (int)s_sched->schedules.size(); s_sched->schedules.push_back(sched); - s_sched->scheduleMap.insert_or_assign(Util::makeUPPER(sched->Name), sched->Num); + s_sched->scheduleMap.insert_or_assign(std::move(Util::makeUPPER(sched->Name)), sched->Num); sched->type = SchedType::Constant; return sched; @@ -245,7 +244,7 @@ namespace Sched { sched->Num = (int)s_sched->schedules.size(); s_sched->schedules.push_back(sched); - s_sched->scheduleMap.insert_or_assign(Util::makeUPPER(sched->Name), sched->Num); + s_sched->scheduleMap.insert_or_assign(std::move(Util::makeUPPER(sched->Name)), sched->Num); sched->type = SchedType::Year; return sched; @@ -261,7 +260,7 @@ namespace Sched { daySched->Num = (int)s_sched->daySchedules.size(); s_sched->daySchedules.push_back(daySched); - s_sched->dayScheduleMap.insert_or_assign(Util::makeUPPER(daySched->Name), daySched->Num); + s_sched->dayScheduleMap.insert_or_assign(std::move(Util::makeUPPER(daySched->Name)), daySched->Num); daySched->tsVals.resize(Constant::iHoursInDay * s_glob->TimeStepsInHour); @@ -277,7 +276,7 @@ namespace Sched { weekSched->Num = (int)s_sched->weekSchedules.size(); s_sched->weekSchedules.push_back(weekSched); - s_sched->weekScheduleMap.insert_or_assign(Util::makeUPPER(weekSched->Name), weekSched->Num); + s_sched->weekScheduleMap.insert_or_assign(std::move(Util::makeUPPER(weekSched->Name)), weekSched->Num); return weekSched; } // AddWeekSchedule() @@ -1375,16 +1374,16 @@ namespace Sched { } } else { for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - int begMinute = 0; - int endMinute = s_glob->MinutesInTimeStep - 1; + int begMin = 0; + int endMin = s_glob->MinutesInTimeStep - 1; for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) { daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = - std::accumulate(minuteVals.begin() + (hr * Constant::iMinutesInHour + begMinute), - minuteVals.begin() + (hr * Constant::iMinutesInHour + endMinute), + std::accumulate(&minuteVals[hr * Constant::iMinutesInHour + begMin], + &minuteVals[hr * Constant::iMinutesInHour + endMin + 1], 0) / double(s_glob->MinutesInTimeStep); daySched->sumTsVals += daySched->tsVals[hr * s_glob->TimeStepsInHour + ts]; - begMinute = endMinute + 1; - endMinute += s_glob->MinutesInTimeStep; + begMin = endMin + 1; + endMin += s_glob->MinutesInTimeStep; } } } @@ -1499,7 +1498,7 @@ namespace Sched { cNumericFields); ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)}; - + if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) { ShowSevereDuplicateName(state, eoh); ErrorsFound = true; @@ -1564,11 +1563,11 @@ namespace Sched { } // is it a sub-hourly schedule or not? - int MinutesPerItem = 60; + int MinutesPerItem = Constant::iMinutesInHour; if (NumNumbers > 3) { MinutesPerItem = int(Numbers(4)); // int NumExpectedItems = 1440 / MinutesPerItem; - if (mod(60, MinutesPerItem) != 0) { + if (mod(Constant::iMinutesInHour, MinutesPerItem) != 0) { ShowSevereCustom(state, eoh, format("Requested {} field value ({}) not evenly divisible into 60", cNumericFields(4), MinutesPerItem)); ErrorsFound = true; continue; @@ -1576,8 +1575,8 @@ namespace Sched { } int numHourlyValues = Numbers(3); - int rowLimitCount = (Numbers(3) * 60.0) / MinutesPerItem; - int hrLimitCount = 60 / MinutesPerItem; + int rowLimitCount = (Numbers(3) * Constant::rMinutesInHour) / MinutesPerItem; + int hrLimitCount = Constant::iMinutesInHour / MinutesPerItem; std::string contextString = format("{}=\"{}\", {}: ", CurrentModuleObject, Alphas(1), cAlphaFields(3)); @@ -1630,7 +1629,7 @@ namespace Sched { if (rowCnt < rowLimitCount) { ShowWarningCustom(state, eoh, format("less than {} hourly values read from file." - "..Number read={}.", numHourlyValues, (rowCnt * 60) / MinutesPerItem)); + "..Number read={}.", numHourlyValues, (rowCnt * Constant::iMinutesInHour) / MinutesPerItem)); } // process the data into the normal schedule data structures @@ -1672,38 +1671,38 @@ namespace Sched { } } else { // Minutes Per Item < 60 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - int endMinute = MinutesPerItem - 1; - int begMinute = 0; + int endMin = MinutesPerItem - 1; + int begMin = 0; for (int NumFields = 1; NumFields <= hrLimitCount; ++NumFields) { - std::fill(&minuteVals[hr * Constant::iMinutesInHour + begMinute], - &minuteVals[hr * Constant::iMinutesInHour + endMinute], + std::fill(&minuteVals[hr * Constant::iMinutesInHour + begMin], + &minuteVals[hr * Constant::iMinutesInHour + endMin + 1], column_values[ifld]); ++ifld; - begMinute = endMinute + 1; - endMinute += MinutesPerItem; + begMin = endMin + 1; + endMin += MinutesPerItem; } } if (FileIntervalInterpolated) { for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - int begMinute = 0; - int endMinute = s_glob->MinutesInTimeStep - 1; + int begMin = 0; + int endMin = s_glob->MinutesInTimeStep - 1; for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) { daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = - std::accumulate(minuteVals.begin() + (hr * Constant::iMinutesInHour + begMinute), - minuteVals.begin() + (hr * Constant::iMinutesInHour + endMinute), + std::accumulate(&minuteVals[hr * Constant::iMinutesInHour + begMin], + &minuteVals[hr * Constant::iMinutesInHour + endMin + 1], 0) / double(s_glob->MinutesInTimeStep); daySched->sumTsVals += daySched->tsVals[hr * s_glob->TimeStepsInHour + ts]; - begMinute = endMinute + 1; - endMinute += s_glob->MinutesInTimeStep; + begMin = endMin + 1; + endMin += s_glob->MinutesInTimeStep; } } } else { for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - int curMinute = s_glob->MinutesInTimeStep - 1; + int curMin = s_glob->MinutesInTimeStep - 1; for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) { - daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = minuteVals[hr * Constant::iMinutesInHour + curMinute]; + daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = minuteVals[hr * Constant::iMinutesInHour + curMin]; daySched->sumTsVals += daySched->tsVals[hr * s_glob->TimeStepsInHour + ts]; - curMinute += s_glob->MinutesInTimeStep; + curMin += s_glob->MinutesInTimeStep; } } } @@ -2777,7 +2776,19 @@ namespace Sched { curValue = StartValue + incrementPerMinute; } - if (begHr == endHr) { + if (begHr > endHr) { + if (begHr == endHr + 1 && begMin == 0 && endMin == Constant::iMinutesInHour - 1) { + ShowWarningError(state, format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, zero time interval detected, {}={}", + ErrContext, DayScheduleName)); + } else { + ShowSevereError(state, + format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, overlapping times detected, {}={}", + ErrContext, + DayScheduleName)); + ErrorsFound = true; + } + + } else if (begHr == endHr) { if (std::find(&setMinuteVals[begHr * Constant::iMinutesInHour + begMin], &setMinuteVals[begHr * Constant::iMinutesInHour + endMin + 1], true) != &setMinuteVals[begHr * Constant::iMinutesInHour + endMin + 1]) { @@ -2809,13 +2820,6 @@ namespace Sched { begMin = 0; } - } else if (endHr < begHr) { - ShowSevereError(state, - format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, overlapping times detected, {}={}", - ErrContext, - DayScheduleName)); - ErrorsFound = true; - } else { // begHr < endHr if (interpolation == Interpolation::Linear) { for (int iMin = begMin; iMin < Constant::iMinutesInHour; ++iMin) { // for portion of starting hour diff --git a/src/EnergyPlus/WeatherManager.cc b/src/EnergyPlus/WeatherManager.cc index 5c8174e9bbb..89b3a7a8047 100644 --- a/src/EnergyPlus/WeatherManager.cc +++ b/src/EnergyPlus/WeatherManager.cc @@ -6005,7 +6005,7 @@ namespace Weather { std::vector const &dayVals = desDayInput.tempRangeSched->getDayVals(state); auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum); for (int hr = 0; hr < Constant::iHoursInDay; ++hr) { - for (int ts = 0; ts <= state.dataGlobal->TimeStepsInHour; ++ts) { + for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) { desDayModEnvrn(ts+1, hr+1).OutDryBulbTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts]; } } diff --git a/src/EnergyPlus/ZoneContaminantPredictorCorrector.cc b/src/EnergyPlus/ZoneContaminantPredictorCorrector.cc index 9e669f6d6a1..013b404048f 100644 --- a/src/EnergyPlus/ZoneContaminantPredictorCorrector.cc +++ b/src/EnergyPlus/ZoneContaminantPredictorCorrector.cc @@ -1108,7 +1108,7 @@ void GetZoneContaminanSetPoints(EnergyPlusData &state) if (state.dataIPShortCut->lAlphaFieldBlanks(5)) { controlledZone.zoneMinCO2Sched = Sched::GetScheduleAlwaysOff(state); } else if ((controlledZone.zoneMinCO2Sched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(5))) == nullptr) { - ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)); + ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)); ErrorsFound = true; } else if (!controlledZone.zoneMinCO2Sched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 2000.0)) { Sched::ShowSevereBadMinMax(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5), @@ -1121,7 +1121,7 @@ void GetZoneContaminanSetPoints(EnergyPlusData &state) if (state.dataIPShortCut->lAlphaFieldBlanks(6)) { controlledZone.zoneMaxCO2Sched = Sched::GetScheduleAlwaysOff(state); } else if ((controlledZone.zoneMaxCO2Sched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(6))) == nullptr) { - ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)); + ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)); ErrorsFound = true; } else if (!controlledZone.zoneMaxCO2Sched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 2000.0)) { Sched::ShowSevereBadMinMax(state, eoh, state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6), diff --git a/testfiles/ASHRAE901_RetailStripmall_STD2019_Denver.idf b/testfiles/ASHRAE901_RetailStripmall_STD2019_Denver.idf index 21ca4ead73d..724fc52a0e8 100644 --- a/testfiles/ASHRAE901_RetailStripmall_STD2019_Denver.idf +++ b/testfiles/ASHRAE901_RetailStripmall_STD2019_Denver.idf @@ -852,7 +852,7 @@ Until: 24:00,23.89, !- Field 23 For: Sunday Holidays AllOtherDays, !- Field 25 Until: 8:00,29.44, !- Field 26 - Until: 8:00,26.67, !- Field 28 + Until: 8:00,26.67, !- Field 28 Is this supposed to be 9:00? Until: 23:00,23.89, !- Field 30 Until: 24:00,23.89; !- Field 32 diff --git a/tst/EnergyPlus/unit/DataHeatBalance.unit.cc b/tst/EnergyPlus/unit/DataHeatBalance.unit.cc index 97059096504..6145bdf3e05 100644 --- a/tst/EnergyPlus/unit/DataHeatBalance.unit.cc +++ b/tst/EnergyPlus/unit/DataHeatBalance.unit.cc @@ -855,7 +855,9 @@ TEST_F(EnergyPlusFixture, DataHeatBalance_CheckConstructLayers) GetEMSInput(*state); // check if EMS actuator is not setup because there is no blind/shade layer SetupWindowShadingControlActuators(*state); - EXPECT_EQ(state->dataRuntimeLang->numEMSActuatorsAvailable, 0); // no EMS actuator because there is shade/blind layer + + // init_state() checks for EMS so there will be actuators for schedules and materials already + EXPECT_EQ(state->dataRuntimeLang->numEMSActuatorsAvailable, 19); // add a blind layer in between glass state->dataConstruction->Construct(4).TotLayers = 5; @@ -884,13 +886,13 @@ TEST_F(EnergyPlusFixture, DataHeatBalance_CheckConstructLayers) state->dataSurface->surfShades(windowSurfNum).blind.movableSlats = true; // check if EMS actuator is available when blind layer is added SetupWindowShadingControlActuators(*state); - EXPECT_EQ(state->dataRuntimeLang->numEMSActuatorsAvailable, 2); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(1).ComponentTypeName, "Window Shading Control"); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(1).ControlTypeName, "Control Status"); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(1).Units, "[ShadeStatus]"); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(2).ComponentTypeName, "Window Shading Control"); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(2).ControlTypeName, "Slat Angle"); - EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(2).Units, "[degrees]"); + EXPECT_EQ(state->dataRuntimeLang->numEMSActuatorsAvailable, 21); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(20).ComponentTypeName, "Window Shading Control"); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(20).ControlTypeName, "Control Status"); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(20).Units, "[ShadeStatus]"); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(21).ComponentTypeName, "Window Shading Control"); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(21).ControlTypeName, "Slat Angle"); + EXPECT_EQ(state->dataRuntimeLang->EMSActuatorAvailable(21).Units, "[degrees]"); } TEST_F(EnergyPlusFixture, DataHeatBalance_setUserTemperatureLocationPerpendicular)