Skip to content

Commit

Permalink
Merge pull request #10658 from NREL/ACoupleAPIEndpoints
Browse files Browse the repository at this point in the history
A couple API Endpoints
  • Loading branch information
Myoldmopar committed Aug 19, 2024
2 parents a59d0b6 + 58ef554 commit 2fda843
Show file tree
Hide file tree
Showing 10 changed files with 755 additions and 13 deletions.
2 changes: 1 addition & 1 deletion scripts/dev/verify_idfs_in_cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
# there are a few files we purposely skip
files_to_skip = {"_1a-Long0.0.idf", "_ExternalInterface-actuator.idf", "_ExternalInterface-schedule.idf",
"_ExternalInterface-variable.idf", "HVAC3Zone-IntGains-Def.imf", "HVAC3ZoneChillerSpec.imf",
"HVAC3ZoneGeometry.imf", "HVAC3ZoneMat-Const.imf"}
"HVAC3ZoneGeometry.imf", "HVAC3ZoneMat-Const.imf", "_1ZoneUncontrolled_ForAPITesting.idf"}
found_idf_files_trimmed = found_idf_files - files_to_skip

# the CMakeLists file will always have "forward" slashes
Expand Down
2 changes: 1 addition & 1 deletion src/EnergyPlus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ if(BUILD_TESTING)
-DEPW_FILE=USA_CO_Golden-NREL.724666_TMY3.epw -P ${PROJECT_SOURCE_DIR}/cmake/RunCallbackTest.cmake)

set(EPW_FILE "${PROJECT_SOURCE_DIR}/weather/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw")
set(IDF_FILE "${PROJECT_SOURCE_DIR}/testfiles/1ZoneUncontrolled.idf")
set(IDF_FILE "${PROJECT_SOURCE_DIR}/testfiles/_1ZoneUncontrolled_ForAPITesting.idf")

add_executable(TestAPI_Functional_C ${PROJECT_SOURCE_DIR}/tst/EnergyPlus/api/TestFunctional.c)
# project_warnings is essentially a C++ interface, and not included here because this is C, not C++
Expand Down
5 changes: 5 additions & 0 deletions src/EnergyPlus/DataRuntimeLanguage.hh
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,11 @@ namespace DataRuntimeLanguage {
struct RuntimeLanguageData : BaseGlobalStruct
{

// In the API, we allow the user to manipulate user-defined EMS globals, but we skip the built-in ones to avoid
// problems. The built-in ones will not always start at zero, so we keep a start/end to ignore that specific range.
int emsVarBuiltInStart = 0;
int emsVarBuiltInEnd = 0;

int NumProgramCallManagers = 0; // count of Erl program managers with calling points
int NumSensors = 0; // count of EMS sensors used in model (data from output variables)
int numActuatorsUsed = 0; // count of EMS actuators used in model
Expand Down
5 changes: 5 additions & 0 deletions src/EnergyPlus/RuntimeLanguageProcessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ void InitializeRuntimeLanguage(EnergyPlusData &state)

if (state.dataRuntimeLangProcessor->InitializeOnce) {

state.dataRuntimeLang->emsVarBuiltInStart = state.dataRuntimeLang->NumErlVariables + 1;

state.dataRuntimeLang->False = SetErlValueNumber(0.0);
state.dataRuntimeLang->True = SetErlValueNumber(1.0);

Expand Down Expand Up @@ -160,6 +162,9 @@ void InitializeRuntimeLanguage(EnergyPlusData &state)
state.dataRuntimeLangProcessor->ActualTimeNum = NewEMSVariable(state, "ACTUALTIME", 0);
state.dataRuntimeLangProcessor->WarmUpFlagNum = NewEMSVariable(state, "WARMUPFLAG", 0);

// update the end of the built-in range so we can ignore those on API calls
state.dataRuntimeLang->emsVarBuiltInEnd = state.dataRuntimeLang->NumErlVariables;

GetRuntimeLanguageUserInput(state); // Load and parse all runtime language objects

date_and_time(datestring, _, _, datevalues);
Expand Down
70 changes: 70 additions & 0 deletions src/EnergyPlus/api/datatransfer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <EnergyPlus/DataGlobalConstants.hh>
#include <EnergyPlus/DataHVACGlobals.hh>
#include <EnergyPlus/DataRuntimeLanguage.hh>
#include <EnergyPlus/DataStringGlobals.hh>
#include <EnergyPlus/HeatBalFiniteDiffManager.hh>
#include <EnergyPlus/InputProcessing/InputProcessor.hh>
#include <EnergyPlus/OutputProcessor.hh>
Expand Down Expand Up @@ -239,6 +240,24 @@ void resetErrorFlag(EnergyPlusState state)
thisState->dataPluginManager->apiErrorFlag = false;
}

char *inputFilePath(EnergyPlusState state)
{
const auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
std::string const path_utf8 = EnergyPlus::FileSystem::toGenericString(thisState->dataStrGlobals->inputFilePath);
char *p = new char[std::strlen(path_utf8.c_str()) + 1];
std::strcpy(p, path_utf8.c_str());
return p;
}

char *epwFilePath(EnergyPlusState state)
{
const auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
std::string const path_utf8 = EnergyPlus::FileSystem::toGenericString(thisState->files.inputWeatherFilePath.filePath);
char *p = new char[std::strlen(path_utf8.c_str()) + 1];
std::strcpy(p, path_utf8.c_str());
return p;
}

char **getObjectNames(EnergyPlusState state, const char *objectType, unsigned int *resultingSize)
{
const auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
Expand Down Expand Up @@ -573,6 +592,57 @@ Real64 getInternalVariableValue(EnergyPlusState state, int handle)
return 0;
}

int getEMSGlobalVariableHandle(EnergyPlusState state, const char *name)
{
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
int index = 0;
for (auto const &erlVar : thisState->dataRuntimeLang->ErlVariable) {
index++;
// only respond if we are outside of the built-in EMS var range
if (index < thisState->dataRuntimeLang->emsVarBuiltInStart || index > thisState->dataRuntimeLang->emsVarBuiltInEnd) {
if (EnergyPlus::Util::SameString(name, erlVar.Name)) {
return index;
}
}
}
return 0;
}

Real64 getEMSGlobalVariableValue(EnergyPlusState state, int handle)
{
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
auto const &erl = thisState->dataRuntimeLang;
bool const insideBuiltInRange = handle >= erl->emsVarBuiltInStart && handle <= erl->emsVarBuiltInEnd;
if (insideBuiltInRange || handle > thisState->dataRuntimeLang->NumErlVariables) {
// need to fatal out once the process is done
// throw an error, set the fatal flag, and then return 0
EnergyPlus::ShowSevereError(
*thisState, fmt::format("Data Exchange API: Problem -- index error in getEMSGlobalVariableValue; received handle: {}", handle));
EnergyPlus::ShowContinueError(
*thisState, "The getEMSGlobalVariableValue function will return 0 for now to allow the process to finish, then EnergyPlus will abort");
thisState->dataPluginManager->apiErrorFlag = true;
return 0;
}
return erl->ErlVariable(handle).Value.Number;
}

void setEMSGlobalVariableValue(EnergyPlusState state, int handle, Real64 value)
{
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
auto const &erl = thisState->dataRuntimeLang;
bool const insideBuiltInRange = handle >= erl->emsVarBuiltInStart && handle <= erl->emsVarBuiltInEnd;
if (insideBuiltInRange || handle > erl->NumErlVariables) {
// need to fatal out once the plugin is done
// throw an error, set the fatal flag, and then return
EnergyPlus::ShowSevereError(
*thisState, fmt::format("Data Exchange API: Problem -- index error in setEMSGlobalVariableValue; received handle: {}", handle));
EnergyPlus::ShowContinueError(*thisState,
"The setEMSGlobalVariableValue function will return to allow the plugin to finish, then EnergyPlus will abort");
thisState->dataPluginManager->apiErrorFlag = true;
}
erl->ErlVariable(handle).Value.Number = value;
}

int getPluginGlobalVariableHandle(EnergyPlusState state, const char *name)
{
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
Expand Down
48 changes: 47 additions & 1 deletion src/EnergyPlus/api/datatransfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ ENERGYPLUSLIB_API int apiErrorFlag(EnergyPlusState state);
/// a calculation to continue, this function can reset the flag.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
ENERGYPLUSLIB_API void resetErrorFlag(EnergyPlusState state);
/// \brief Provides the input file path back to the user
/// \details In most circumstances the client will know the path to the input file, but there are some cases where code
/// is generalized in unexpected workflows. Users have requested a way to get the input file path back from the running instance.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
/// \return A char * of the input file path. This allocates a new char *, and calling clients must free this when done with it!
ENERGYPLUSLIB_API char *inputFilePath(EnergyPlusState state);
/// \brief Provides the weather file path back to the user
/// \details In most circumstances the client will know the path to the weather file, but there are some cases where code
/// is generalized in unexpected workflows. Users have requested a way to get the weather file path back from the running instance.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
/// \return A char * of the weather file path. This allocates a new char *, and calling clients must free this when done with it!
ENERGYPLUSLIB_API char *epwFilePath(EnergyPlusState state);

// ----- DATA TRANSFER HELPER FUNCTIONS

Expand Down Expand Up @@ -149,7 +161,7 @@ ENERGYPLUSLIB_API void freeAPIData(const struct APIDataEntry *data, unsigned int
ENERGYPLUSLIB_API char **getObjectNames(EnergyPlusState state, const char *objectType, unsigned int *resultingSize);
/// \brief Clears an object names array allocation
/// \details This function frees an instance of the object names array, which is returned from getObjectNames
/// \param[in] data An array (pointer) of const char * as returned from the getObjectNames function
/// \param[in] objectNames An array (pointer) of char * as returned from the getObjectNames function
/// \param[in] arraySize The size of the object name array, which is known after the call to getObjectNames.
/// \return Nothing, this simply frees the memory
ENERGYPLUSLIB_API void freeObjectNames(char **objectNames, unsigned int arraySize);
Expand Down Expand Up @@ -297,6 +309,40 @@ ENERGYPLUSLIB_API int getInternalVariableHandle(EnergyPlusState state, const cha
/// \see getInternalVariableHandle
ENERGYPLUSLIB_API Real64 getInternalVariableValue(EnergyPlusState state, int handle);

// ----- FUNCTIONS RELATED TO EMS GLOBAL VARIABLES (FOR CORNER CASES WHERE PLUGIN/API BLENDS WITH EMS PROGRAMS)
/// \brief Gets a handle to an EMS "Global" variable
/// \details When using EMS, it is sometimes necessary to share data between programs.
/// EMS global variables are declared in the input file and used in EMS programs.
/// EMS global variables are identified by name only. This function returns -1 if a match is not found.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
/// \param[in] name The name of the EMS global variable, which is declared in the EnergyPlus input file
/// \return The integer handle to an EMS global variable, or -1 if a match is not found.
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
/// \see apiDataFullyReady
ENERGYPLUSLIB_API int getEMSGlobalVariableHandle(EnergyPlusState state, const char *name);
/// \brief Gets the current value of an EMS "Global" variable
/// \details When using EMS, the value of the shared "global" variables can change at any time.
/// This function returns the current value of the variable.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
/// \param[in] handle The handle id to an EMS "Global" variable, which can be retrieved using the `getEMSGlobalVariableHandle` function.
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
/// \return The current value of the variable, in floating point form, or zero if a handle problem is encountered. If a zero
/// is returned, use the `apiErrorFlag` function to disambiguate the return value.
/// \see apiDataFullyReady
/// \see getEMSGlobalVariableHandle
ENERGYPLUSLIB_API Real64 getEMSGlobalVariableValue(EnergyPlusState state, int handle);
/// \brief Sets the value of an EMS "Global" variable
/// \details When using EMS, the value of the shared "global" variables can change at any time.
/// This function sets the variable to a new value.
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
/// \param[in] handle The handle id to an EMS "Global" variable, which can be retrieved using the `getEMSGlobalVariableHandle` function.
/// \param[in] value The floating point value to be assigned to the global variable
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
/// \remark A handle index or other problem will return 0 and set a flag to cause EnergyPlus to terminate once Python completes.
/// \see apiDataFullyReady
/// \see getEMSGlobalVariableHandle
ENERGYPLUSLIB_API void setEMSGlobalVariableValue(EnergyPlusState state, int handle, Real64 value);

// ----- FUNCTIONS RELATED TO PYTHON PLUGIN GLOBAL VARIABLES (ONLY USED FOR PYTHON PLUGIN SYSTEM)

/// \brief Gets a handle to a Python Plugin "Global" variable
Expand Down
Loading

6 comments on commit 2fda843

@nrel-bot-3
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - x86_64-MacOS-10.18-clang-15.0.0: Tests Failed (2 of 2872 tests passed, 0 test warnings)

Failures:\n

API Test Summary

  • Failed: 10
  • notrun: 5

ConvertInputFormat Test Summary

  • Failed: 4
  • notrun: 1

integration Test Summary

  • Passed: 2
  • Failed: 794

Build Badge Test Badge

@nrel-bot-2c
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - x86_64-Linux-Ubuntu-22.04-gcc-11.4: OK (2893 of 2893 tests passed, 0 test warnings)

Build Badge Test Badge

@nrel-bot
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - Win64-Windows-10-VisualStudio-16: OK (2871 of 2871 tests passed, 0 test warnings)

Build Badge Test Badge

@nrel-bot-2
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - x86_64-Linux-Ubuntu-22.04-gcc-11.4-IntegrationCoverage-Debug: OK (797 of 797 tests passed, 0 test warnings)

Build Badge Test Badge Coverage Badge

@nrel-bot-2b
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - x86_64-Linux-Ubuntu-22.04-gcc-11.4-UnitTestsCoverage-Debug: OK (2077 of 2077 tests passed, 0 test warnings)

Build Badge Test Badge Coverage Badge

@nrel-bot-3
Copy link

Choose a reason for hiding this comment

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

develop (Myoldmopar) - x86_64-MacOS-10.18-clang-15.0.0: OK (2866 of 2872 tests passed, 0 test warnings)

Failures:\n

EnergyPlusFixture Test Summary

  • Passed: 1578
  • SEGFAULT: 5
  • Subprocess aborted: 1

Build Badge Test Badge

Please sign in to comment.