Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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(EnergyPlusData &state, EMSManager::EMSCallFrom iCalledFrom, const std::function<void(void *)> &f);
86 : void runAnyRegisteredCallbacks(EnergyPlusData &state, EMSManager::EMSCallFrom iCalledFrom, bool &anyRan);
87 : void onBeginEnvironment(EnergyPlusData &state);
88 : std::string pythonStringForUsage(EnergyPlusData &state);
89 :
90 : void clear_state();
91 :
92 146 : struct PluginInstance
93 : {
94 44 : PluginInstance(const fs::path &_modulePath, const std::string &_className, std::string emsName, bool runPluginDuringWarmup)
95 88 : : modulePath(_modulePath), className(_className), emsAlias(std::move(emsName)), runDuringWarmup(runPluginDuringWarmup),
96 88 : stringIdentifier(_modulePath.string() + "." + _className)
97 : {
98 44 : }
99 :
100 : // members
101 : fs::path modulePath;
102 : std::string className;
103 : std::string emsAlias;
104 : bool runDuringWarmup;
105 : std::string stringIdentifier; // for diagnostic reporting
106 :
107 : // setup/shutdown should only be called once construction is completely done, i.e., setup() should only be called once the vector holding all
108 : // 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
109 : // happen inside setup() and shutdown() are related to un-managed memory, and it's tricky to manage inside existing constructor/move
110 : // operations, so they are split out into these explicitly called methods.
111 : void setup(EnergyPlusData &state);
112 : void shutdown() const;
113 :
114 : // methods
115 : static void reportPythonError(EnergyPlusData &state);
116 : bool run(EnergyPlusData &state, EMSManager::EMSCallFrom iCallingPoint) const; // calls main() on this plugin instance
117 :
118 : // plugin calling point hooks
119 : const char *sHookBeginNewEnvironment = "on_begin_new_environment";
120 : const char *sHookBeginZoneTimestepBeforeSetCurrentWeather = "on_begin_zone_timestep_before_set_current_weather";
121 : const char *sHookAfterNewEnvironmentWarmUpIsComplete = "on_after_new_environment_warmup_is_complete";
122 : const char *sHookBeginZoneTimestepBeforeInitHeatBalance = "on_begin_zone_timestep_before_init_heat_balance";
123 : const char *sHookBeginZoneTimestepAfterInitHeatBalance = "on_begin_zone_timestep_after_init_heat_balance";
124 : const char *sHookBeginTimestepBeforePredictor = "on_begin_timestep_before_predictor";
125 : const char *sHookAfterPredictorBeforeHVACManagers = "on_after_predictor_before_hvac_managers";
126 : const char *sHookAfterPredictorAfterHVACManagers = "on_after_predictor_after_hvac_managers";
127 : const char *sHookInsideHVACSystemIterationLoop = "on_inside_hvac_system_iteration_loop";
128 : const char *sHookEndOfZoneTimestepBeforeZoneReporting = "on_end_of_zone_timestep_before_zone_reporting";
129 : const char *sHookEndOfZoneTimestepAfterZoneReporting = "on_end_of_zone_timestep_after_zone_reporting";
130 : const char *sHookEndOfSystemTimestepBeforeHVACReporting = "on_end_of_system_timestep_before_hvac_reporting";
131 : const char *sHookEndOfSystemTimestepAfterHVACReporting = "on_end_of_system_timestep_after_hvac_reporting";
132 : const char *sHookEndOfZoneSizing = "on_end_of_zone_sizing";
133 : const char *sHookEndOfSystemSizing = "on_end_of_system_sizing";
134 : const char *sHookAfterComponentInputReadIn = "on_end_of_component_input_read_in";
135 : const char *sHookUserDefinedComponentModel = "on_user_defined_component_model";
136 : const char *sHookUnitarySystemSizing = "on_unitary_system_sizing";
137 : bool bHasBeginNewEnvironment = false;
138 : bool bHasBeginZoneTimestepBeforeSetCurrentWeather = false;
139 : bool bHasAfterNewEnvironmentWarmUpIsComplete = false;
140 : bool bHasBeginZoneTimestepBeforeInitHeatBalance = false;
141 : bool bHasBeginZoneTimestepAfterInitHeatBalance = false;
142 : bool bHasBeginTimestepBeforePredictor = false;
143 : bool bHasAfterPredictorBeforeHVACManagers = false;
144 : bool bHasAfterPredictorAfterHVACManagers = false;
145 : bool bHasInsideHVACSystemIterationLoop = false;
146 : bool bHasEndOfZoneTimestepBeforeZoneReporting = false;
147 : bool bHasEndOfZoneTimestepAfterZoneReporting = false;
148 : bool bHasEndOfSystemTimestepBeforeHVACReporting = false;
149 : bool bHasEndOfSystemTimestepAfterHVACReporting = false;
150 : bool bHasEndOfZoneSizing = false;
151 : bool bHasEndOfSystemSizing = false;
152 : bool bHasAfterComponentInputReadIn = false;
153 : bool bHasUserDefinedComponentModel = false;
154 : bool bHasUnitarySystemSizing = false;
155 : #if LINK_WITH_PYTHON
156 : PyObject *pModule = nullptr; // reference to module
157 : PyObject *pClassInstance = nullptr; // reference to instantiated class -- *don't decref until the end of the simulation*
158 : // precalculated function names as PyObjects
159 : PyObject *pBeginNewEnvironment = nullptr;
160 : PyObject *pBeginZoneTimestepBeforeSetCurrentWeather = nullptr;
161 : PyObject *pAfterNewEnvironmentWarmUpIsComplete = nullptr;
162 : PyObject *pBeginZoneTimestepBeforeInitHeatBalance = nullptr;
163 : PyObject *pBeginZoneTimestepAfterInitHeatBalance = nullptr;
164 : PyObject *pBeginTimestepBeforePredictor = nullptr;
165 : PyObject *pAfterPredictorBeforeHVACManagers = nullptr;
166 : PyObject *pAfterPredictorAfterHVACManagers = nullptr;
167 : PyObject *pInsideHVACSystemIterationLoop = nullptr;
168 : PyObject *pEndOfZoneTimestepBeforeZoneReporting = nullptr;
169 : PyObject *pEndOfZoneTimestepAfterZoneReporting = nullptr;
170 : PyObject *pEndOfSystemTimestepBeforeHVACReporting = nullptr;
171 : PyObject *pEndOfSystemTimestepAfterHVACReporting = nullptr;
172 : PyObject *pEndOfZoneSizing = nullptr;
173 : PyObject *pEndOfSystemSizing = nullptr;
174 : PyObject *pAfterComponentInputReadIn = nullptr;
175 : PyObject *pUserDefinedComponentModel = nullptr;
176 : PyObject *pUnitarySystemSizing = nullptr;
177 : #endif
178 : };
179 :
180 : class PluginManager
181 : {
182 : public:
183 : explicit PluginManager(EnergyPlusData &state);
184 : ~PluginManager();
185 :
186 : static int numActiveCallbacks(EnergyPlusData &state);
187 : static void addToPythonPath(EnergyPlusData &state, const fs::path &path, bool userDefinedPath);
188 : static fs::path sanitizedPath(fs::path const &path);
189 : static void setupOutputVariables(EnergyPlusData &state);
190 :
191 : int maxGlobalVariableIndex = -1;
192 : void addGlobalVariable(EnergyPlusData &state, const std::string &name);
193 : static int getGlobalVariableHandle(EnergyPlusData &state, const std::string &name, bool suppress_warning = false);
194 : static Real64 getGlobalVariableValue(EnergyPlusData &state, int handle);
195 : static void setGlobalVariableValue(EnergyPlusData &state, int handle, Real64 value);
196 :
197 : int maxTrendVariableIndex = -1;
198 : static int getTrendVariableHandle(EnergyPlusData &state, const std::string &name);
199 : static Real64 getTrendVariableValue(EnergyPlusData &state, int handle, int timeIndex);
200 : static size_t getTrendVariableHistorySize(EnergyPlusData &state, int handle);
201 : static Real64 getTrendVariableAverage(EnergyPlusData &state, int handle, int count);
202 : static Real64 getTrendVariableMin(EnergyPlusData &state, int handle, int count);
203 : static Real64 getTrendVariableMax(EnergyPlusData &state, int handle, int count);
204 : static Real64 getTrendVariableSum(EnergyPlusData &state, int handle, int count);
205 : static Real64 getTrendVariableDirection(EnergyPlusData &state, int handle, int count);
206 :
207 : static void updatePluginValues(EnergyPlusData &state);
208 :
209 : static int getLocationOfUserDefinedPlugin(EnergyPlusData &state, std::string const &_programName);
210 : static void runSingleUserDefinedPlugin(EnergyPlusData &state, int index);
211 : static bool anyUnexpectedPluginObjects(EnergyPlusData &state);
212 :
213 : bool eplusRunningViaPythonAPI = false;
214 : };
215 :
216 6 : struct PluginTrendVariable
217 : {
218 : std::string name;
219 : int numValues;
220 : std::deque<Real64> values;
221 : std::deque<Real64> times;
222 : int indexOfPluginVariable;
223 : PluginTrendVariable(EnergyPlusData &state, std::string _name, int _numValues, int _indexOfPluginVariable);
224 6 : void reset()
225 : {
226 6 : this->values.clear();
227 2406 : for (int i = 1; i <= this->numValues; i++) {
228 2400 : this->values.push_back(0);
229 : }
230 6 : }
231 : };
232 :
233 : } // namespace PluginManagement
234 :
235 1542 : struct PluginManagerData : BaseGlobalStruct
236 : {
237 : std::map<EMSManager::EMSCallFrom, std::vector<std::function<void(void *)>>> callbacks;
238 : std::unique_ptr<PluginManagement::PluginManager> pluginManager;
239 : std::vector<PluginManagement::PluginTrendVariable> trends;
240 : std::vector<PluginManagement::PluginInstance> plugins;
241 :
242 : std::vector<std::string> globalVariableNames;
243 : std::vector<Real64> globalVariableValues;
244 : bool fullyReady = false;
245 : bool apiErrorFlag = false;
246 : std::vector<std::string> const objectsToFind = {
247 : "PythonPlugin:OutputVariable", "PythonPlugin:SearchPaths", "PythonPlugin:Instance", "PythonPlugin:Variables", "PythonPlugin:TrendVariable"};
248 :
249 : bool eplusRunningViaPythonAPI = false;
250 :
251 0 : void clear_state() override
252 : {
253 0 : callbacks.clear();
254 : #if LINK_WITH_PYTHON
255 0 : for (auto &plugin : plugins) {
256 0 : plugin.shutdown(); // clear unmanaged memory first
257 : }
258 0 : trends.clear();
259 0 : globalVariableNames.clear();
260 0 : globalVariableValues.clear();
261 0 : plugins.clear();
262 0 : fullyReady = false;
263 0 : apiErrorFlag = false;
264 0 : auto *p = pluginManager.release();
265 0 : delete p;
266 : #endif
267 0 : }
268 : };
269 :
270 : } // namespace EnergyPlus
271 :
272 : #endif // EPLUS_PLUGIN_MANAGER_HH
|