Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : #ifndef EPLUS_PLUGIN_MANAGER_HH
49 : #define EPLUS_PLUGIN_MANAGER_HH
50 :
51 : // C++ Headers
52 : #include <iomanip>
53 : #include <queue>
54 : #include <utility>
55 : #include <vector>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/DataGlobals.hh>
59 : #include <EnergyPlus/EMSManager.hh>
60 : #include <EnergyPlus/EnergyPlus.hh>
61 :
62 : #if LINK_WITH_PYTHON
63 : #ifdef _DEBUG
64 : // We don't want to try to import a debug build of Python here
65 : // so if we are building a Debug build of the C++ code, we need
66 : // to undefine _DEBUG during the #include command for Python.h.
67 : // Otherwise it will fail
68 : #undef _DEBUG
69 : #include <Python.h>
70 : #define _DEBUG
71 : #else
72 : #include <Python.h>
73 : #endif
74 : #endif
75 :
76 : namespace EnergyPlus {
77 :
78 : // Forward declarations
79 : struct EnergyPlusData;
80 :
81 : namespace PluginManagement {
82 :
83 : constexpr const char *programName = "python";
84 :
85 : void registerNewCallback(const EnergyPlusData &state, EMSManager::EMSCallFrom iCalledFrom, const std::function<void(void *)> &f);
86 : void registerUserDefinedCallback(const EnergyPlusData &state, const std::function<void(void *)> &f, const std::string &programNameInInputFile);
87 :
88 : void runAnyRegisteredCallbacks(EnergyPlusData &state, EMSManager::EMSCallFrom iCalledFrom, bool &anyRan);
89 : void onBeginEnvironment(const EnergyPlusData &state);
90 : std::string pythonStringForUsage(const EnergyPlusData &state);
91 :
92 : void clear_state();
93 :
94 : struct PluginInstance
95 : {
96 : PluginInstance(const fs::path &_modulePath, const std::string &_className, std::string emsName, bool runPluginDuringWarmup);
97 :
98 : // members
99 : fs::path modulePath;
100 : std::string className;
101 : std::string emsAlias;
102 : bool runDuringWarmup;
103 : std::string stringIdentifier; // for diagnostic reporting
104 :
105 : // setup/shutdown should only be called once construction is completely done, i.e., setup() should only be called once the vector holding all
106 : // the instances is done for the day, and shutdown should only be called when you are ready to destruct all the instances. The things that
107 : // happen inside setup() and shutdown() are related to un-managed memory, and it's tricky to manage inside existing constructor/move
108 : // operations, so they are split out into these explicitly called methods.
109 : void setup(EnergyPlusData &state);
110 : void shutdown() const;
111 :
112 : // methods
113 : static void reportPythonError(EnergyPlusData &state);
114 : bool run(EnergyPlusData &state, EMSManager::EMSCallFrom iCallingPoint) const; // calls main() on this plugin instance
115 :
116 : // plugin calling point hooks
117 : const char *sHookBeginNewEnvironment = "on_begin_new_environment";
118 : const char *sHookBeginZoneTimestepBeforeSetCurrentWeather = "on_begin_zone_timestep_before_set_current_weather";
119 : const char *sHookAfterNewEnvironmentWarmUpIsComplete = "on_after_new_environment_warmup_is_complete";
120 : const char *sHookBeginZoneTimestepBeforeInitHeatBalance = "on_begin_zone_timestep_before_init_heat_balance";
121 : const char *sHookBeginZoneTimestepAfterInitHeatBalance = "on_begin_zone_timestep_after_init_heat_balance";
122 : const char *sHookBeginTimestepBeforePredictor = "on_begin_timestep_before_predictor";
123 : const char *sHookAfterPredictorBeforeHVACManagers = "on_after_predictor_before_hvac_managers";
124 : const char *sHookAfterPredictorAfterHVACManagers = "on_after_predictor_after_hvac_managers";
125 : const char *sHookInsideHVACSystemIterationLoop = "on_inside_hvac_system_iteration_loop";
126 : const char *sHookEndOfZoneTimestepBeforeZoneReporting = "on_end_of_zone_timestep_before_zone_reporting";
127 : const char *sHookEndOfZoneTimestepAfterZoneReporting = "on_end_of_zone_timestep_after_zone_reporting";
128 : const char *sHookEndOfSystemTimestepBeforeHVACReporting = "on_end_of_system_timestep_before_hvac_reporting";
129 : const char *sHookEndOfSystemTimestepAfterHVACReporting = "on_end_of_system_timestep_after_hvac_reporting";
130 : const char *sHookEndOfZoneSizing = "on_end_of_zone_sizing";
131 : const char *sHookEndOfSystemSizing = "on_end_of_system_sizing";
132 : const char *sHookAfterComponentInputReadIn = "on_end_of_component_input_read_in";
133 : const char *sHookUserDefinedComponentModel = "on_user_defined_component_model";
134 : const char *sHookUnitarySystemSizing = "on_unitary_system_sizing";
135 : bool bHasBeginNewEnvironment = false;
136 : bool bHasBeginZoneTimestepBeforeSetCurrentWeather = false;
137 : bool bHasAfterNewEnvironmentWarmUpIsComplete = false;
138 : bool bHasBeginZoneTimestepBeforeInitHeatBalance = false;
139 : bool bHasBeginZoneTimestepAfterInitHeatBalance = false;
140 : bool bHasBeginTimestepBeforePredictor = false;
141 : bool bHasAfterPredictorBeforeHVACManagers = false;
142 : bool bHasAfterPredictorAfterHVACManagers = false;
143 : bool bHasInsideHVACSystemIterationLoop = false;
144 : bool bHasEndOfZoneTimestepBeforeZoneReporting = false;
145 : bool bHasEndOfZoneTimestepAfterZoneReporting = false;
146 : bool bHasEndOfSystemTimestepBeforeHVACReporting = false;
147 : bool bHasEndOfSystemTimestepAfterHVACReporting = false;
148 : bool bHasEndOfZoneSizing = false;
149 : bool bHasEndOfSystemSizing = false;
150 : bool bHasAfterComponentInputReadIn = false;
151 : bool bHasUserDefinedComponentModel = false;
152 : bool bHasUnitarySystemSizing = false;
153 : #if LINK_WITH_PYTHON
154 : PyObject *pModule = nullptr; // reference to module
155 : PyObject *pClassInstance = nullptr; // reference to instantiated class -- *don't decref until the end of the simulation*
156 : // precalculated function names as PyObjects
157 : PyObject *pBeginNewEnvironment = nullptr;
158 : PyObject *pBeginZoneTimestepBeforeSetCurrentWeather = nullptr;
159 : PyObject *pAfterNewEnvironmentWarmUpIsComplete = nullptr;
160 : PyObject *pBeginZoneTimestepBeforeInitHeatBalance = nullptr;
161 : PyObject *pBeginZoneTimestepAfterInitHeatBalance = nullptr;
162 : PyObject *pBeginTimestepBeforePredictor = nullptr;
163 : PyObject *pAfterPredictorBeforeHVACManagers = nullptr;
164 : PyObject *pAfterPredictorAfterHVACManagers = nullptr;
165 : PyObject *pInsideHVACSystemIterationLoop = nullptr;
166 : PyObject *pEndOfZoneTimestepBeforeZoneReporting = nullptr;
167 : PyObject *pEndOfZoneTimestepAfterZoneReporting = nullptr;
168 : PyObject *pEndOfSystemTimestepBeforeHVACReporting = nullptr;
169 : PyObject *pEndOfSystemTimestepAfterHVACReporting = nullptr;
170 : PyObject *pEndOfZoneSizing = nullptr;
171 : PyObject *pEndOfSystemSizing = nullptr;
172 : PyObject *pAfterComponentInputReadIn = nullptr;
173 : PyObject *pUserDefinedComponentModel = nullptr;
174 : PyObject *pUnitarySystemSizing = nullptr;
175 : #endif
176 : };
177 :
178 : class PluginManager
179 : {
180 : public:
181 : explicit PluginManager(EnergyPlusData &state);
182 : ~PluginManager();
183 :
184 : static int numActiveCallbacks(const EnergyPlusData &state);
185 : static void addToPythonPath(EnergyPlusData &state, const fs::path &includePath, bool userDefinedPath);
186 : static void setupOutputVariables(EnergyPlusData &state);
187 :
188 : int maxGlobalVariableIndex = -1;
189 : void addGlobalVariable(const EnergyPlusData &state, const std::string &name);
190 : static int getGlobalVariableHandle(EnergyPlusData &state, const std::string &name, bool suppress_warning = false);
191 : static Real64 getGlobalVariableValue(EnergyPlusData &state, int handle);
192 : static void setGlobalVariableValue(EnergyPlusData &state, int handle, Real64 value);
193 :
194 : int maxTrendVariableIndex = -1;
195 : static int getTrendVariableHandle(const EnergyPlusData &state, const std::string &name);
196 : static Real64 getTrendVariableValue(const EnergyPlusData &state, int handle, int timeIndex);
197 : static size_t getTrendVariableHistorySize(const EnergyPlusData &state, int handle);
198 : static Real64 getTrendVariableAverage(const EnergyPlusData &state, int handle, int count);
199 : static Real64 getTrendVariableMin(const EnergyPlusData &state, int handle, int count);
200 : static Real64 getTrendVariableMax(const EnergyPlusData &state, int handle, int count);
201 : static Real64 getTrendVariableSum(const EnergyPlusData &state, int handle, int count);
202 : static Real64 getTrendVariableDirection(const EnergyPlusData &state, int handle, int count);
203 :
204 : static void updatePluginValues(EnergyPlusData &state);
205 :
206 : static int getLocationOfUserDefinedPlugin(const EnergyPlusData &state, std::string const &_programName);
207 : static int getUserDefinedCallbackIndex(const EnergyPlusData &state, const std::string &callbackProgramName);
208 : static void runSingleUserDefinedPlugin(EnergyPlusData &state, int index);
209 : static void runSingleUserDefinedCallback(EnergyPlusData &state, int index);
210 : static bool anyUnexpectedPluginObjects(EnergyPlusData &state);
211 :
212 : bool eplusRunningViaPythonAPI = false;
213 :
214 : // For debugging purposes / issuing better error messages
215 : static std::vector<std::string> currentPythonPath();
216 : };
217 :
218 : struct PluginTrendVariable
219 : {
220 : std::string name;
221 : int numValues;
222 : std::deque<Real64> values;
223 : std::deque<Real64> times;
224 : int indexOfPluginVariable;
225 : PluginTrendVariable(const EnergyPlusData &state, std::string _name, int _numValues, int _indexOfPluginVariable);
226 6 : void reset()
227 : {
228 6 : this->values.clear();
229 2406 : for (int i = 1; i <= this->numValues; i++) {
230 2400 : this->values.push_back(0);
231 : }
232 6 : }
233 : };
234 :
235 : } // namespace PluginManagement
236 :
237 : struct PluginManagerData : BaseGlobalStruct
238 : {
239 : std::map<EMSManager::EMSCallFrom, std::vector<std::function<void(void *)>>> callbacks;
240 : std::vector<std::string> userDefinedCallbackNames;
241 : std::vector<std::function<void(void *)>> userDefinedCallbacks;
242 : std::unique_ptr<PluginManagement::PluginManager> pluginManager;
243 : std::vector<PluginManagement::PluginTrendVariable> trends;
244 : std::vector<PluginManagement::PluginInstance> plugins;
245 :
246 : std::vector<std::string> globalVariableNames;
247 : std::vector<Real64> globalVariableValues;
248 : bool fullyReady = false;
249 : bool apiErrorFlag = false;
250 : std::vector<std::string> const objectsToFind = {
251 : "PythonPlugin:OutputVariable", "PythonPlugin:SearchPaths", "PythonPlugin:Instance", "PythonPlugin:Variables", "PythonPlugin:TrendVariable"};
252 :
253 : bool eplusRunningViaPythonAPI = false;
254 :
255 796 : void init_state([[maybe_unused]] EnergyPlusData &state) override
256 : {
257 796 : }
258 :
259 0 : void clear_state() override
260 : {
261 0 : callbacks.clear();
262 0 : userDefinedCallbackNames.clear();
263 0 : userDefinedCallbacks.clear();
264 : #if LINK_WITH_PYTHON
265 0 : for (auto &plugin : plugins) {
266 0 : plugin.shutdown(); // clear unmanaged memory first
267 0 : }
268 0 : trends.clear();
269 0 : globalVariableNames.clear();
270 0 : globalVariableValues.clear();
271 0 : plugins.clear();
272 0 : fullyReady = false;
273 0 : apiErrorFlag = false;
274 0 : auto *p = pluginManager.release();
275 0 : delete p;
276 : #endif
277 0 : }
278 : };
279 :
280 : } // namespace EnergyPlus
281 :
282 : #endif // EPLUS_PLUGIN_MANAGER_HH
|