LCOV - code coverage report
Current view: top level - EnergyPlus - PluginManager.hh (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 75.0 % 24 18
Test Date: 2025-05-22 16:09:37 Functions: 75.0 % 4 3

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

Generated by: LCOV version 2.0-1