diff --git a/scripts/dev/verify_idfs_in_cmake.py b/scripts/dev/verify_idfs_in_cmake.py index 2c48a107401..2adaa1b45ce 100755 --- a/scripts/dev/verify_idfs_in_cmake.py +++ b/scripts/dev/verify_idfs_in_cmake.py @@ -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 diff --git a/src/EnergyPlus/CMakeLists.txt b/src/EnergyPlus/CMakeLists.txt index 9f4f3565624..cfb928e00dc 100644 --- a/src/EnergyPlus/CMakeLists.txt +++ b/src/EnergyPlus/CMakeLists.txt @@ -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++ diff --git a/src/EnergyPlus/DataRuntimeLanguage.hh b/src/EnergyPlus/DataRuntimeLanguage.hh index dbfb131cda2..85a0424dad6 100644 --- a/src/EnergyPlus/DataRuntimeLanguage.hh +++ b/src/EnergyPlus/DataRuntimeLanguage.hh @@ -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 diff --git a/src/EnergyPlus/RuntimeLanguageProcessor.cc b/src/EnergyPlus/RuntimeLanguageProcessor.cc index 71214b90f17..55dc9cd6380 100644 --- a/src/EnergyPlus/RuntimeLanguageProcessor.cc +++ b/src/EnergyPlus/RuntimeLanguageProcessor.cc @@ -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); @@ -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); diff --git a/src/EnergyPlus/api/datatransfer.cc b/src/EnergyPlus/api/datatransfer.cc index 196f1e2b406..25b274b469e 100644 --- a/src/EnergyPlus/api/datatransfer.cc +++ b/src/EnergyPlus/api/datatransfer.cc @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,24 @@ void resetErrorFlag(EnergyPlusState state) thisState->dataPluginManager->apiErrorFlag = false; } +char *inputFilePath(EnergyPlusState state) +{ + const auto *thisState = static_cast(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(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(state); @@ -573,6 +592,57 @@ Real64 getInternalVariableValue(EnergyPlusState state, int handle) return 0; } +int getEMSGlobalVariableHandle(EnergyPlusState state, const char *name) +{ + auto *thisState = static_cast(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(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(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(state); diff --git a/src/EnergyPlus/api/datatransfer.h b/src/EnergyPlus/api/datatransfer.h index ae45b8fc5c8..7b31ba56970 100644 --- a/src/EnergyPlus/api/datatransfer.h +++ b/src/EnergyPlus/api/datatransfer.h @@ -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 @@ -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); @@ -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 diff --git a/src/EnergyPlus/api/datatransfer.py b/src/EnergyPlus/api/datatransfer.py index 3b154108d38..0a1c9112d28 100644 --- a/src/EnergyPlus/api/datatransfer.py +++ b/src/EnergyPlus/api/datatransfer.py @@ -56,7 +56,7 @@ from ctypes import cdll, c_int, c_char_p, c_void_p, POINTER, Structure, byref from pyenergyplus.common import RealEP, EnergyPlusException, is_number from typing import List, Union - +from pathlib import Path class DataExchange: """ @@ -139,6 +139,10 @@ def __init__(self, api: cdll, running_as_python_plugin: bool = False): self.api.apiErrorFlag.restype = c_int self.api.resetErrorFlag.argtypes = [c_void_p] self.api.resetErrorFlag.restype = c_void_p + self.api.inputFilePath.argtypes = [c_void_p] + self.api.inputFilePath.restype = c_char_p + self.api.epwFilePath.argtypes = [c_void_p] + self.api.epwFilePath.restype = c_char_p self.api.requestVariable.argtypes = [c_void_p, c_char_p, c_char_p] self.api.getNumNodesInCondFDSurfaceLayer.argtypes = [c_void_p, c_char_p, c_char_p] self.api.requestVariable.restype = c_void_p @@ -201,6 +205,12 @@ def __init__(self, api: cdll, running_as_python_plugin: bool = False): self.api.currentEnvironmentNum.restype = c_int self.api.warmupFlag.argtypes = [c_void_p] self.api.warmupFlag.restype = c_int + self.api.getEMSGlobalVariableHandle.argtypes = [c_void_p, c_char_p] + self.api.getEMSGlobalVariableHandle.restype = c_int + self.api.getEMSGlobalVariableValue.argtypes = [c_void_p, c_int] + self.api.getEMSGlobalVariableValue.restype = RealEP + self.api.setEMSGlobalVariableValue.argtypes = [c_void_p, c_int, RealEP] + self.api.setEMSGlobalVariableValue.restype = c_void_p self.api.getPluginGlobalVariableHandle.argtypes = [c_void_p, c_char_p] self.api.getPluginGlobalVariableHandle.restype = c_int self.api.getPluginGlobalVariableValue.argtypes = [c_void_p, c_int] @@ -353,6 +363,33 @@ def reset_api_error_flag(self, state: c_void_p) -> None: """ self.api.resetErrorFlag(state) + def get_input_file_path(self, state: c_void_p) -> Path: + """ + Provides the input file path back to the client. 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 state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. + :return: A pathlib.Path of the input file path + """ + c_string = self.api.inputFilePath(state) + res = Path(c_string.decode('utf-8')) + return res + + def get_weather_file_path(self, state: c_void_p) -> Path: + """ + Provides the weather file path back to the client. 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 state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. + :return: A pathlib.Path of the weather file + """ + c_string = self.api.epwFilePath(state) + res = Path(c_string.decode('utf-8')) + return res + + def get_object_names(self, state: c_void_p, object_type_name: Union[str, bytes]) -> List[str]: """ Gets the instance names for a given object type in the current input file @@ -698,6 +735,82 @@ def get_construction_handle(self, state: c_void_p, var_name: Union[str, bytes]) "'{}'".format(var_name)) return self.api.getConstructionHandle(state, var_name) + def get_ems_global_handle(self, state: c_void_p, var_name: Union[str, bytes]) -> int: + """ + Get a handle to an EMS global variable in a running simulation. + + EMS global variables are used as a way to share data between running EMS programs. First a global variable must + be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been + declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client + should get a handle to the variable using this get_global_handle function, then + using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are + floating point values. + + The arguments passed into this function do not need to be a particular case, as the EnergyPlus API + automatically converts values to upper-case when finding matches to internal variables in the simulation. + + Note also that the arguments passed in here can be either strings or bytes, as this wrapper handles conversion + as needed. + + :param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. + :param var_name: The name of the EMS global variable to retrieve, this name must be listed in an IDF object: + `EnergyManagementSystem:GlobalVariable` + :return: An integer ID for this EMS global variable, or -1 if one could not be found. + """ + if isinstance(var_name, str): + var_name = var_name.encode('utf-8') + elif not isinstance(var_name, bytes): + raise EnergyPlusException( + "`get_ems_global_handle` expects `component_type` as a `str` or UTF-8 encoded `bytes`, not " + "'{}'".format(var_name)) + return self.api.getEMSGlobalVariableHandle(state, var_name) + + def get_ems_global_value(self, state: c_void_p, handle: int) -> float: + """ + Get the current value of an EMS global variable in a running simulation. + + EMS global variables are used as a way to share data between running EMS programs. First a global variable must + be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been + declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client + should get a handle to the variable using this get_global_handle function, then + using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are + floating point values. + + :param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. + :param handle: An integer returned from the `get_ems_global_handle` function. + :return: Floating point representation of the EMS global variable value + """ + if not is_number(handle): + raise EnergyPlusException( + "`get_ems_global_value` expects `handle` as an `int`, not " + "'{}'".format(handle)) + return self.api.getEMSGlobalVariableValue(state, handle) + + def set_ems_global_value(self, state: c_void_p, handle: int, value: float) -> None: + """ + Set the current value of an EMS global variable in a running simulation. + + EMS global variables are used as a way to share data between running EMS programs. First a global variable must + be declared in the input file using the EnergyManagementSystem:GlobalVariable object. Once a name has been + declared, it can be accessed by EMS programs by name, and through the Python API. For API usage, the client + should get a handle to the variable using this get_global_handle function, then + using the get_ems_global_value and set_ems_global_value functions as needed. Note all global variables are + floating point values. + + :param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. + :param handle: An integer returned from the `get_ems_global_handle` function. + :param value: Floating point value to assign to the EMS global variable + """ + if not is_number(handle): + raise EnergyPlusException( + "`set_ems_global_value` expects `variable_handle` as an `int`, not " + "'{}'".format(handle)) + if not is_number(value): + raise EnergyPlusException( + "`set_ems_global_value` expects `value` as a `float`, not " + "'{}'".format(value)) + self.api.setEMSGlobalVariableValue(state, handle, value) + def get_global_handle(self, state: c_void_p, var_name: Union[str, bytes]) -> int: """ Get a handle to a global variable in a running simulation. This is only used for Python Plugin applications! @@ -740,12 +853,6 @@ def get_global_value(self, state: c_void_p, handle: int) -> float: using this get_global_value and the set_global_value functions as needed. Note all global variables are floating point values. - The arguments passed into this function do not need to be a particular case, as the EnergyPlus API - automatically converts values to upper-case when finding matches to internal variables in the simulation. - - Note also that the arguments passed in here can be either strings or bytes, as this wrapper handles conversion - as needed. - :param state: An active EnergyPlus "state" that is returned from a call to `api.state_manager.new_state()`. :param handle: An integer returned from the `get_global_handle` function. :return: Floating point representation of the global variable value diff --git a/testfiles/_1ZoneUncontrolled_ForAPITesting.idf b/testfiles/_1ZoneUncontrolled_ForAPITesting.idf new file mode 100644 index 00000000000..371220f4605 --- /dev/null +++ b/testfiles/_1ZoneUncontrolled_ForAPITesting.idf @@ -0,0 +1,466 @@ +!-Generator IDFEditor 1.34 +!-Option OriginalOrderTop UseSpecialFormat +!-NOTE: All comments with '!-' are ignored by the IDFEditor and are generated automatically. +!- Use '!' comments if they need to be retained when using the IDFEditor. +!1ZoneUncontrolled.idf +! Basic file description: Basic test for EnergyPlus. Resistive Walls. Regular (no ground contact) floor. +! Regular roof. No Windows. +! +! Highlights: Very basic test to see that EnergyPlus "works". +! +! +! Simulation Location/Run: Denver Centennial Golden CO USA WMO=724666, 2 design days, 1 run period, +! Run Control executes two design days (see RUN PERIOD object) +! +! Location: Denver, CO +! +! Design Days: Denver Centennial Golden CO USA Annual Heating 99%, MaxDB=-15.5°C +! Denver Centennial Golden CO USA Annual Cooling (DB=>MWB) 1%, MaxDB=32°C MWB=15.5°C +! +! Run Period (Weather File): Full Annual Simulation, DENVER_STAPLETON_CO_USA_WMO_724690 +! +! Run Control: No zone or system sizing, design day run control (no weather file simulation) +! +! Building: Fictional 1 zone building with resistive walls. +! +! The building is oriented due north. +! +! Floor Area: 232.25 m2 +! Number of Stories: 1 +! +! Zone Description Details: +! +! (0,15.24,0) (15.24,15.24,0) +! _____________________________ +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! | | +! |_____________________________| +! +! (0,0,0) (15.24,0,0) +! +! Internal gains description: NA +! +! Interzone Surfaces: None +! Internal Mass: None +! People: None +! Lights: None +! Equipment: None +! Windows: 0 +! Detached Shading: None +! Daylight: None +! Natural Ventilation: None +! Compact Schedules: NA (Example of non-Compact Schedules) +! Solar Distribution: MinimalShadowing +! +! HVAC: NA +! +! Zonal Equipment: NA +! Central Air Handling Equipment: No +! System Equipment Autosize: No +! Purchased Cooling: No +! Purchased Heating: No +! Purchased Chilled Water: No +! Purchased Hot Water: No +! Coils: None +! Pumps: None +! Boilers: None +! Chillers: None +! Towers: None +! +! Results: +! Standard Reports: Variable Dictionary, Surfaces (dxf-wireframe), Meter File +! Timestep or Hourly Variables: Hourly and Daily +! Time bins Report: None +! HTML Report: None +! Environmental Emissions: None +! Utility Tariffs: None + + Version,24.2; + + Timestep,4; + + Building, + Simple One Zone (Wireframe DXF), !- Name + 0, !- North Axis {deg} + Suburbs, !- Terrain + 0.04, !- Loads Convergence Tolerance Value {W} + 0.004, !- Temperature Convergence Tolerance Value {deltaC} + MinimalShadowing, !- Solar Distribution + 30, !- Maximum Number of Warmup Days + 6; !- Minimum Number of Warmup Days + + HeatBalanceAlgorithm,ConductionTransferFunction; + + SurfaceConvectionAlgorithm:Inside,TARP; + + SurfaceConvectionAlgorithm:Outside,DOE-2; + + SimulationControl, + No, !- Do Zone Sizing Calculation + No, !- Do System Sizing Calculation + No, !- Do Plant Sizing Calculation + Yes, !- Run Simulation for Sizing Periods + Yes, !- Run Simulation for Weather File Run Periods + No, !- Do HVAC Sizing Simulation for Sizing Periods + 1; !- Maximum Number of HVAC Sizing Simulation Passes + + RunPeriod, + Run Period 1, !- Name + 1, !- Begin Month + 1, !- Begin Day of Month + , !- Begin Year + 12, !- End Month + 31, !- End Day of Month + , !- End Year + Tuesday, !- Day of Week for Start Day + Yes, !- Use Weather File Holidays and Special Days + Yes, !- Use Weather File Daylight Saving Period + No, !- Apply Weekend Holiday Rule + Yes, !- Use Weather File Rain Indicators + Yes; !- Use Weather File Snow Indicators + + Site:Location, + Denver Centennial Golden N_CO_USA Design_Conditions, !- Name + 39.74, !- Latitude {deg} + -105.18, !- Longitude {deg} + -7.00, !- Time Zone {hr} + 1829.00; !- Elevation {m} + + ! WMO=724666 Time Zone=NAM: (GMT-07:00) Mountain Time (US & Canada) + ! Data Source=ASHRAE 2009 Annual Design Conditions + ! Denver Centennial Golden N_CO_USA Annual Heating Design Conditions Wind Speed=3m/s Wind Dir=340 + ! Coldest Month=DEC + ! Denver Centennial Golden N_CO_USA Annual Heating 99%, MaxDB=-15.5°C + + SizingPeriod:DesignDay, + Denver Centennial Golden N Ann Htg 99% Condns DB, !- Name + 12, !- Month + 21, !- Day of Month + WinterDesignDay, !- Day Type + -15.5, !- Maximum Dry-Bulb Temperature {C} + 0.0, !- Daily Dry-Bulb Temperature Range {deltaC} + , !- Dry-Bulb Temperature Range Modifier Type + , !- Dry-Bulb Temperature Range Modifier Day Schedule Name + Wetbulb, !- Humidity Condition Type + -15.5, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C} + , !- Humidity Condition Day Schedule Name + , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir} + , !- Enthalpy at Maximum Dry-Bulb {J/kg} + , !- Daily Wet-Bulb Temperature Range {deltaC} + 81198., !- Barometric Pressure {Pa} + 3, !- Wind Speed {m/s} + 340, !- Wind Direction {deg} + No, !- Rain Indicator + No, !- Snow Indicator + No, !- Daylight Saving Time Indicator + ASHRAEClearSky, !- Solar Model Indicator + , !- Beam Solar Day Schedule Name + , !- Diffuse Solar Day Schedule Name + , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless} + , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless} + 0.00; !- Sky Clearness + + ! Denver Centennial Golden N Annual Cooling Design Conditions Wind Speed=4.9m/s Wind Dir=0 + ! Hottest Month=JUL + ! Denver Centennial Golden N_CO_USA Annual Cooling (DB=>MWB) 1%, MaxDB=32°C MWB=15.5°C + + SizingPeriod:DesignDay, + Denver Centennial Golden N Ann Clg 1% Condns DB=>MWB, !- Name + 7, !- Month + 21, !- Day of Month + SummerDesignDay, !- Day Type + 32, !- Maximum Dry-Bulb Temperature {C} + 15.2, !- Daily Dry-Bulb Temperature Range {deltaC} + , !- Dry-Bulb Temperature Range Modifier Type + , !- Dry-Bulb Temperature Range Modifier Day Schedule Name + Wetbulb, !- Humidity Condition Type + 15.5, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C} + , !- Humidity Condition Day Schedule Name + , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir} + , !- Enthalpy at Maximum Dry-Bulb {J/kg} + , !- Daily Wet-Bulb Temperature Range {deltaC} + 81198., !- Barometric Pressure {Pa} + 4.9, !- Wind Speed {m/s} + 0, !- Wind Direction {deg} + No, !- Rain Indicator + No, !- Snow Indicator + No, !- Daylight Saving Time Indicator + ASHRAEClearSky, !- Solar Model Indicator + , !- Beam Solar Day Schedule Name + , !- Diffuse Solar Day Schedule Name + , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless} + , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless} + 1.00; !- Sky Clearness + + Material:NoMass, + R13LAYER, !- Name + Rough, !- Roughness + 2.290965, !- Thermal Resistance {m2-K/W} + 0.9000000, !- Thermal Absorptance + 0.7500000, !- Solar Absorptance + 0.7500000; !- Visible Absorptance + + Material:NoMass, + R31LAYER, !- Name + Rough, !- Roughness + 5.456, !- Thermal Resistance {m2-K/W} + 0.9000000, !- Thermal Absorptance + 0.7500000, !- Solar Absorptance + 0.7500000; !- Visible Absorptance + + Material, + C5 - 4 IN HW CONCRETE, !- Name + MediumRough, !- Roughness + 0.1014984, !- Thickness {m} + 1.729577, !- Conductivity {W/m-K} + 2242.585, !- Density {kg/m3} + 836.8000, !- Specific Heat {J/kg-K} + 0.9000000, !- Thermal Absorptance + 0.6500000, !- Solar Absorptance + 0.6500000; !- Visible Absorptance + + Construction, + R13WALL, !- Name + R13LAYER; !- Outside Layer + + Construction, + FLOOR, !- Name + C5 - 4 IN HW CONCRETE; !- Outside Layer + + Construction, + ROOF31, !- Name + R31LAYER; !- Outside Layer + + Zone, + ZONE ONE, !- Name + 0, !- Direction of Relative North {deg} + 0, !- X Origin {m} + 0, !- Y Origin {m} + 0, !- Z Origin {m} + 1, !- Type + 1, !- Multiplier + autocalculate, !- Ceiling Height {m} + autocalculate; !- Volume {m3} + + ScheduleTypeLimits, + Fraction, !- Name + 0.0, !- Lower Limit Value + 1.0, !- Upper Limit Value + CONTINUOUS; !- Numeric Type + + GlobalGeometryRules, + UpperLeftCorner, !- Starting Vertex Position + CounterClockWise, !- Vertex Entry Direction + World; !- Coordinate System + + BuildingSurface:Detailed, + Zn001:Wall001, !- Name + Wall, !- Surface Type + R13WALL, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Outdoors, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + SunExposed, !- Sun Exposure + WindExposed, !- Wind Exposure + 0.5000000, !- View Factor to Ground + 4, !- Number of Vertices + 0,0,4.572000, !- X,Y,Z ==> Vertex 1 {m} + 0,0,0, !- X,Y,Z ==> Vertex 2 {m} + 15.24000,0,0, !- X,Y,Z ==> Vertex 3 {m} + 15.24000,0,4.572000; !- X,Y,Z ==> Vertex 4 {m} + + BuildingSurface:Detailed, + Zn001:Wall002, !- Name + Wall, !- Surface Type + R13WALL, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Outdoors, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + SunExposed, !- Sun Exposure + WindExposed, !- Wind Exposure + 0.5000000, !- View Factor to Ground + 4, !- Number of Vertices + 15.24000,0,4.572000, !- X,Y,Z ==> Vertex 1 {m} + 15.24000,0,0, !- X,Y,Z ==> Vertex 2 {m} + 15.24000,15.24000,0, !- X,Y,Z ==> Vertex 3 {m} + 15.24000,15.24000,4.572000; !- X,Y,Z ==> Vertex 4 {m} + + BuildingSurface:Detailed, + Zn001:Wall003, !- Name + Wall, !- Surface Type + R13WALL, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Outdoors, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + SunExposed, !- Sun Exposure + WindExposed, !- Wind Exposure + 0.5000000, !- View Factor to Ground + 4, !- Number of Vertices + 15.24000,15.24000,4.572000, !- X,Y,Z ==> Vertex 1 {m} + 15.24000,15.24000,0, !- X,Y,Z ==> Vertex 2 {m} + 0,15.24000,0, !- X,Y,Z ==> Vertex 3 {m} + 0,15.24000,4.572000; !- X,Y,Z ==> Vertex 4 {m} + + BuildingSurface:Detailed, + Zn001:Wall004, !- Name + Wall, !- Surface Type + R13WALL, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Outdoors, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + SunExposed, !- Sun Exposure + WindExposed, !- Wind Exposure + 0.5000000, !- View Factor to Ground + 4, !- Number of Vertices + 0,15.24000,4.572000, !- X,Y,Z ==> Vertex 1 {m} + 0,15.24000,0, !- X,Y,Z ==> Vertex 2 {m} + 0,0,0, !- X,Y,Z ==> Vertex 3 {m} + 0,0,4.572000; !- X,Y,Z ==> Vertex 4 {m} + + BuildingSurface:Detailed, + Zn001:Flr001, !- Name + Floor, !- Surface Type + FLOOR, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Adiabatic, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + NoSun, !- Sun Exposure + NoWind, !- Wind Exposure + 1.000000, !- View Factor to Ground + 4, !- Number of Vertices + 15.24000,0.000000,0.0, !- X,Y,Z ==> Vertex 1 {m} + 0.000000,0.000000,0.0, !- X,Y,Z ==> Vertex 2 {m} + 0.000000,15.24000,0.0, !- X,Y,Z ==> Vertex 3 {m} + 15.24000,15.24000,0.0; !- X,Y,Z ==> Vertex 4 {m} + + BuildingSurface:Detailed, + Zn001:Roof001, !- Name + Roof, !- Surface Type + ROOF31, !- Construction Name + ZONE ONE, !- Zone Name + , !- Space Name + Outdoors, !- Outside Boundary Condition + , !- Outside Boundary Condition Object + SunExposed, !- Sun Exposure + WindExposed, !- Wind Exposure + 0, !- View Factor to Ground + 4, !- Number of Vertices + 0.000000,15.24000,4.572, !- X,Y,Z ==> Vertex 1 {m} + 0.000000,0.000000,4.572, !- X,Y,Z ==> Vertex 2 {m} + 15.24000,0.000000,4.572, !- X,Y,Z ==> Vertex 3 {m} + 15.24000,15.24000,4.572; !- X,Y,Z ==> Vertex 4 {m} + + Output:Variable,*,Site Outdoor Air Drybulb Temperature,hourly; + + Output:Variable,*,Site Total Sky Cover,hourly; + + Output:Variable,*,Site Opaque Sky Cover,hourly; + + Output:Variable,*,Site Daylight Saving Time Status,daily; + + Output:Variable,*,Site Day Type Index,daily; + + Output:Variable,*,Zone Mean Air Temperature,hourly; + + Output:Variable,*,Zone Operative Temperature,hourly; !- Zone Average [C] + + Output:Variable,ZONE ONE,Zone Wetbulb Globe Temperature,hourly; + + Output:Variable,*,Zone Total Internal Latent Gain Energy,hourly; + + Output:Variable,*,Zone Mean Radiant Temperature,hourly; + + Output:Variable,*,Zone Air Heat Balance Surface Convection Rate,hourly; + + Output:Variable,*,Zone Air Heat Balance Air Energy Storage Rate,hourly; + + Output:Variable,*,Surface Inside Face Temperature,daily; + + Output:Variable,*,Surface Outside Face Temperature,daily; + + Output:Variable,*,Surface Inside Face Convection Heat Transfer Coefficient,daily; + + Output:Variable,*,Surface Outside Face Convection Heat Transfer Coefficient,daily; + + Output:Variable,*,Other Equipment Total Heating Energy,monthly; + + Output:Variable,*,Zone Other Equipment Total Heating Energy,monthly; + + Output:VariableDictionary,IDF; + + Output:Surfaces:Drawing,dxf:wireframe; + + Output:Constructions,Constructions; + + Output:Meter:MeterFileOnly,ExteriorLights:Electricity,hourly; + + Output:Meter:MeterFileOnly,EnergyTransfer:Building,hourly; + + Output:Meter:MeterFileOnly,EnergyTransfer:Facility,hourly; + + OutputControl:Table:Style, + ALL; !- Column Separator + + Output:Table:SummaryReports, + AllSummary; !- Report 1 Name + + Exterior:Lights, + ExtLights, !- Name + AlwaysOn, !- Schedule Name + 5250, !- Design Level {W} + AstronomicalClock, !- Control Option + Grounds Lights; !- End-Use Subcategory + + ScheduleTypeLimits, + On/Off, !- Name + 0, !- Lower Limit Value + 1, !- Upper Limit Value + DISCRETE; !- Numeric Type + + OtherEquipment, + Test 352a, !- Name + None, !- Fuel Type + ZONE ONE, !- Zone or ZoneList or Space or SpaceList Name + AlwaysOn, !- Schedule Name + EquipmentLevel, !- Design Level Calculation Method + 352, !- Design Level {W} + , !- Power per Zone Floor Area {W/m2} + , !- Power per Person {W/person} + 0, !- Fraction Latent + 0, !- Fraction Radiant + 0; !- Fraction Lost + + OtherEquipment, + Test 352 minus, !- Name + None, !- Fuel Type + ZONE ONE, !- Zone or ZoneList or Space or SpaceList Name + AlwaysOn, !- Schedule Name + EquipmentLevel, !- Design Level Calculation Method + -352, !- Design Level {W} + , !- Power per Zone Floor Area {W/m2} + , !- Power per Person {W/person} + 0, !- Fraction Latent + 0, !- Fraction Radiant + 0; !- Fraction Lost + + Schedule:Constant,AlwaysOn,On/Off,1.0; + + EnergyManagementSystem:GlobalVariable,MaximumEffort; diff --git a/tst/EnergyPlus/api/TestDataTransfer.c b/tst/EnergyPlus/api/TestDataTransfer.c index 3980ba06440..978d1bbde5f 100644 --- a/tst/EnergyPlus/api/TestDataTransfer.c +++ b/tst/EnergyPlus/api/TestDataTransfer.c @@ -79,7 +79,7 @@ void afterZoneTimeStepHandler(EnergyPlusState state) char **surfaceNames = getObjectNames(state, "BuildingSurface:Detailed", &arraySize); if (arraySize == 0) { - printf("Encountered a file with no BuildingSurface:Detailed, can't run this script on that file! Aborting!"); + printf("Encountered a file with no BuildingSurface:Detailed, can't run this script on that file! Aborting!\n"); exit(1); } @@ -91,11 +91,27 @@ void afterZoneTimeStepHandler(EnergyPlusState state) wallConstruction = getConstructionHandle(state, "R13WALL"); floorConstruction = getConstructionHandle(state, "FLOOR"); - // don't forget to free memory from the API! + // checking for EMS globals + int const emsGlobalValid = getEMSGlobalVariableHandle(state, "MaximumEffort"); + int const emsGlobalInvalid = getEMSGlobalVariableHandle(state, "4or5moments"); + int const emsGlobalBuiltIn = getEMSGlobalVariableHandle(state, "WARMUPFLAG"); + if (emsGlobalValid > 0 && emsGlobalInvalid == 0 && emsGlobalBuiltIn == 0) { + printf("EMS Global handle lookups worked just fine!\n"); + } else { + printf("EMS Global handle lookup failed. Make sure to call this with _1ZoneUncontrolled_ForAPITesting.idf\n"); + exit(1); + } + setEMSGlobalVariableValue(state, emsGlobalValid, 2.0); + Real64 const val = getEMSGlobalVariableValue(state, emsGlobalValid); + if (val < 1.9999 || val > 2.0001) { + printf("EMS Global assignment/lookup didn't seem to work, odd\n"); + exit(1); + } + freeAPIData(data, arraySize); freeObjectNames(surfaceNames, arraySize); - printf("Got handles %d, %d, %d, %d, %d, %d", + printf("Got handles %d, %d, %d, %d, %d, %d\n", outdoorDewPointActuator, outdoorTempSensor, outdoorDewPointSensor, @@ -106,6 +122,13 @@ void afterZoneTimeStepHandler(EnergyPlusState state) wallConstruction == -1 || floorConstruction == -1) { exit(1); } + + char *idfPath = inputFilePath(state); + char *epwPath = epwFilePath(state); + printf("Got an input file path of: %s, and weather file path of: %s\n", idfPath, epwPath); + free(idfPath); + free(epwPath); + handlesRetrieved = 1; } setActuatorValue(state, outdoorDewPointActuator, -25.0); diff --git a/tst/EnergyPlus/api/TestDataTransfer.py b/tst/EnergyPlus/api/TestDataTransfer.py index a73a91b1107..422670190d7 100644 --- a/tst/EnergyPlus/api/TestDataTransfer.py +++ b/tst/EnergyPlus/api/TestDataTransfer.py @@ -101,6 +101,26 @@ def time_step_handler(state): wall_construction_handle = api.exchange.get_construction_handle(state, "R13WALL") floor_construction_handle = api.exchange.get_construction_handle(state, "FLOOR") + + idf_path = api.exchange.get_input_file_path(state) + epw_path = api.exchange.get_weather_file_path(state) + print(f"Got an input file path of: {idf_path}, and weather file path of: {epw_path}") + + # checking for EMS globals + ems_global_valid = api.exchange.get_ems_global_handle(state, "MaximumEffort") + ems_global_invalid = api.exchange.get_ems_global_handle(state, "4or5moments") + ems_global_builtin = api.exchange.get_ems_global_handle(state, "WARMUPFLAG") + if ems_global_valid > 0 and ems_global_invalid == 0 and ems_global_builtin == 0: + print("EMS Global handle lookups worked just fine!") + else: + raise Exception( + "EMS Global handle lookup failed. Make sure to call this with _1ZoneUncontrolled_ForAPITesting.idf" + ) + api.exchange.set_ems_global_value(state, ems_global_valid, 2.0) + val = api.exchange.get_ems_global_value(state, ems_global_valid) + if val < 1.9999 or val > 2.0001: + raise Exception("EMS Global assignment/lookup didn't seem to work, odd") + except Exception as e: # Capture ok and exception message exception = e