Skip to content

Commit c1a2396

Browse files
committed
Add API endpoints to access EMS global variables
1 parent 15f6491 commit c1a2396

File tree

3 files changed

+161
-7
lines changed

3 files changed

+161
-7
lines changed

src/EnergyPlus/api/datatransfer.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,50 @@ Real64 getInternalVariableValue(EnergyPlusState state, int handle)
573573
return 0;
574574
}
575575

576+
int getEMSGlobalVariableHandle(EnergyPlusState state, const char *name)
577+
{
578+
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
579+
int index = 0;
580+
for (auto const &erlVar : thisState->dataRuntimeLang->ErlVariable) {
581+
index++;
582+
if (EnergyPlus::Util::SameString(name, erlVar.Name)) {
583+
return index;
584+
}
585+
}
586+
return 0;
587+
}
588+
589+
Real64 getEMSGlobalVariableValue(EnergyPlusState state, int handle)
590+
{
591+
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
592+
if (handle < 0 || handle > thisState->dataRuntimeLang->NumErlVariables) {
593+
// need to fatal out once the process is done
594+
// throw an error, set the fatal flag, and then return 0
595+
EnergyPlus::ShowSevereError(
596+
*thisState, fmt::format("Data Exchange API: Problem -- index error in getEMSGlobalVariableValue; received handle: {}", handle));
597+
EnergyPlus::ShowContinueError(
598+
*thisState, "The getEMSGlobalVariableValue function will return 0 for now to allow the process to finish, then EnergyPlus will abort");
599+
thisState->dataPluginManager->apiErrorFlag = true;
600+
return 0;
601+
}
602+
return thisState->dataRuntimeLang->ErlVariable(handle).Value.Number;
603+
}
604+
605+
void setEMSGlobalVariableValue(EnergyPlusState state, int handle, Real64 value)
606+
{
607+
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);
608+
if (handle < 0 || handle > thisState->dataRuntimeLang->NumErlVariables) {
609+
// need to fatal out once the plugin is done
610+
// throw an error, set the fatal flag, and then return
611+
EnergyPlus::ShowSevereError(
612+
*thisState, fmt::format("Data Exchange API: Problem -- index error in setEMSGlobalVariableValue; received handle: {}", handle));
613+
EnergyPlus::ShowContinueError(*thisState,
614+
"The setEMSGlobalVariableValue function will return to allow the plugin to finish, then EnergyPlus will abort");
615+
thisState->dataPluginManager->apiErrorFlag = true;
616+
}
617+
thisState->dataRuntimeLang->ErlVariable(handle).Value.Number = value;
618+
}
619+
576620
int getPluginGlobalVariableHandle(EnergyPlusState state, const char *name)
577621
{
578622
auto *thisState = static_cast<EnergyPlus::EnergyPlusData *>(state);

src/EnergyPlus/api/datatransfer.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ ENERGYPLUSLIB_API void freeAPIData(const struct APIDataEntry *data, unsigned int
149149
ENERGYPLUSLIB_API char **getObjectNames(EnergyPlusState state, const char *objectType, unsigned int *resultingSize);
150150
/// \brief Clears an object names array allocation
151151
/// \details This function frees an instance of the object names array, which is returned from getObjectNames
152-
/// \param[in] data An array (pointer) of const char * as returned from the getObjectNames function
152+
/// \param[in] objectNames An array (pointer) of char * as returned from the getObjectNames function
153153
/// \param[in] arraySize The size of the object name array, which is known after the call to getObjectNames.
154154
/// \return Nothing, this simply frees the memory
155155
ENERGYPLUSLIB_API void freeObjectNames(char **objectNames, unsigned int arraySize);
@@ -297,6 +297,40 @@ ENERGYPLUSLIB_API int getInternalVariableHandle(EnergyPlusState state, const cha
297297
/// \see getInternalVariableHandle
298298
ENERGYPLUSLIB_API Real64 getInternalVariableValue(EnergyPlusState state, int handle);
299299

300+
// ----- FUNCTIONS RELATED TO EMS GLOBAL VARIABLES (FOR CORNER CASES WHERE PLUGIN/API BLENDS WITH EMS PROGRAMS)
301+
/// \brief Gets a handle to an EMS "Global" variable
302+
/// \details When using EMS, it is sometimes necessary to share data between programs.
303+
/// EMS global variables are declared in the input file and used in EMS programs.
304+
/// EMS global variables are identified by name only. This function returns -1 if a match is not found.
305+
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
306+
/// \param[in] name The name of the EMS global variable, which is declared in the EnergyPlus input file
307+
/// \return The integer handle to an EMS global variable, or -1 if a match is not found.
308+
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
309+
/// \see apiDataFullyReady
310+
ENERGYPLUSLIB_API int getEMSGlobalVariableHandle(EnergyPlusState state, const char *name);
311+
/// \brief Gets the current value of an EMS "Global" variable
312+
/// \details When using EMS, the value of the shared "global" variables can change at any time.
313+
/// This function returns the current value of the variable.
314+
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
315+
/// \param[in] handle The handle id to an EMS "Global" variable, which can be retrieved using the `getEMSGlobalVariableHandle` function.
316+
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
317+
/// \return The current value of the variable, in floating point form, or zero if a handle problem is encountered. If a zero
318+
/// is returned, use the `apiErrorFlag` function to disambiguate the return value.
319+
/// \see apiDataFullyReady
320+
/// \see getEMSGlobalVariableHandle
321+
ENERGYPLUSLIB_API Real64 getEMSGlobalVariableValue(EnergyPlusState state, int handle);
322+
/// \brief Sets the value of an EMS "Global" variable
323+
/// \details When using EMS, the value of the shared "global" variables can change at any time.
324+
/// This function sets the variable to a new value.
325+
/// \param[in] state An active EnergyPlusState instance created with `stateNew`.
326+
/// \param[in] handle The handle id to an EMS "Global" variable, which can be retrieved using the `getEMSGlobalVariableHandle` function.
327+
/// \param[in] value The floating point value to be assigned to the global variable
328+
/// \remark The behavior of this function is not well-defined until the `apiDataFullyReady` function returns true.
329+
/// \remark A handle index or other problem will return 0 and set a flag to cause EnergyPlus to terminate once Python completes.
330+
/// \see apiDataFullyReady
331+
/// \see getEMSGlobalVariableHandle
332+
ENERGYPLUSLIB_API void setEMSGlobalVariableValue(EnergyPlusState state, int handle, Real64 value);
333+
300334
// ----- FUNCTIONS RELATED TO PYTHON PLUGIN GLOBAL VARIABLES (ONLY USED FOR PYTHON PLUGIN SYSTEM)
301335

302336
/// \brief Gets a handle to a Python Plugin "Global" variable

src/EnergyPlus/api/datatransfer.py

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ def __init__(self, api: cdll, running_as_python_plugin: bool = False):
201201
self.api.currentEnvironmentNum.restype = c_int
202202
self.api.warmupFlag.argtypes = [c_void_p]
203203
self.api.warmupFlag.restype = c_int
204+
self.api.getEMSGlobalVariableHandle.argtypes = [c_void_p, c_char_p]
205+
self.api.getEMSGlobalVariableHandle.restype = c_int
206+
self.api.getEMSGlobalVariableValue.argtypes = [c_void_p, c_int]
207+
self.api.getEMSGlobalVariableValue.restype = RealEP
208+
self.api.setEMSGlobalVariableValue.argtypes = [c_void_p, c_int, RealEP]
209+
self.api.setEMSGlobalVariableValue.restype = c_void_p
204210
self.api.getPluginGlobalVariableHandle.argtypes = [c_void_p, c_char_p]
205211
self.api.getPluginGlobalVariableHandle.restype = c_int
206212
self.api.getPluginGlobalVariableValue.argtypes = [c_void_p, c_int]
@@ -698,6 +704,82 @@ def get_construction_handle(self, state: c_void_p, var_name: Union[str, bytes])
698704
"'{}'".format(var_name))
699705
return self.api.getConstructionHandle(state, var_name)
700706

707+
def get_ems_global_handle(self, state: c_void_p, var_name: Union[str, bytes]) -> int:
708+
"""
709+
Get a handle to an EMS global variable in a running simulation.
710+
711+
EMS global variables are used as a way to share data between running EMS programs. First a global variable must
712+
be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been
713+
declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client
714+
should get a handle to the variable using this get_global_handle function, then
715+
using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are
716+
floating point values.
717+
718+
The arguments passed into this function do not need to be a particular case, as the EnergyPlus API
719+
automatically converts values to upper-case when finding matches to internal variables in the simulation.
720+
721+
Note also that the arguments passed in here can be either strings or bytes, as this wrapper handles conversion
722+
as needed.
723+
724+
:param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`.
725+
:param var_name: The name of the EMS global variable to retrieve, this name must be listed in an IDF object:
726+
`EnergyManagementSystem:GlobalVariable`
727+
:return: An integer ID for this EMS global variable, or -1 if one could not be found.
728+
"""
729+
if isinstance(var_name, str):
730+
var_name = var_name.encode('utf-8')
731+
elif not isinstance(var_name, bytes):
732+
raise EnergyPlusException(
733+
"`get_ems_global_handle` expects `component_type` as a `str` or UTF-8 encoded `bytes`, not "
734+
"'{}'".format(var_name))
735+
return self.api.getEMSGlobalVariableHandle(state, var_name)
736+
737+
def get_ems_global_value(self, state: c_void_p, handle: int) -> float:
738+
"""
739+
Get the current value of an EMS global variable in a running simulation.
740+
741+
EMS global variables are used as a way to share data between running EMS programs. First a global variable must
742+
be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been
743+
declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client
744+
should get a handle to the variable using this get_global_handle function, then
745+
using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are
746+
floating point values.
747+
748+
:param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`.
749+
:param handle: An integer returned from the `get_ems_global_handle` function.
750+
:return: Floating point representation of the EMS global variable value
751+
"""
752+
if not is_number(handle):
753+
raise EnergyPlusException(
754+
"`get_ems_global_value` expects `handle` as an `int`, not "
755+
"'{}'".format(handle))
756+
return self.api.getEMSGlobalVariableValue(state, handle)
757+
758+
def set_ems_global_value(self, state: c_void_p, handle: int, value: float) -> None:
759+
"""
760+
Set the current value of an EMS global variable in a running simulation.
761+
762+
EMS global variables are used as a way to share data between running EMS programs. First a global variable must
763+
be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been
764+
declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client
765+
should get a handle to the variable using this get_global_handle function, then
766+
using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are
767+
floating point values.
768+
769+
:param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`.
770+
:param handle: An integer returned from the `get_ems_global_handle` function.
771+
:param value: Floating point value to assign to the EMS global variable
772+
"""
773+
if not is_number(handle):
774+
raise EnergyPlusException(
775+
"`set_ems_global_value` expects `variable_handle` as an `int`, not "
776+
"'{}'".format(handle))
777+
if not is_number(value):
778+
raise EnergyPlusException(
779+
"`set_ems_global_value` expects `value` as a `float`, not "
780+
"'{}'".format(value))
781+
self.api.setEMSGlobalVariableValue(state, handle, value)
782+
701783
def get_global_handle(self, state: c_void_p, var_name: Union[str, bytes]) -> int:
702784
"""
703785
Get a handle to a global variable in a running simulation. This is only used for Python Plugin applications!
@@ -740,12 +822,6 @@ def get_global_value(self, state: c_void_p, handle: int) -> float:
740822
using this get_global_value and the set_global_value functions as needed. Note all global variables are
741823
floating point values.
742824
743-
The arguments passed into this function do not need to be a particular case, as the EnergyPlus API
744-
automatically converts values to upper-case when finding matches to internal variables in the simulation.
745-
746-
Note also that the arguments passed in here can be either strings or bytes, as this wrapper handles conversion
747-
as needed.
748-
749825
:param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`.
750826
:param handle: An integer returned from the `get_global_handle` function.
751827
:return: Floating point representation of the global variable value

0 commit comments

Comments
 (0)