LCOV - code coverage report
Current view: top level - EnergyPlus - GeneratorDynamicsManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 201 426 47.2 %
Date: 2023-01-17 19:17:23 Functions: 6 6 100.0 %

          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             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Array.functions.hh>
      53             : #include <ObjexxFCL/Fmath.hh>
      54             : 
      55             : // EnergyPlus Headers
      56             : #include <EnergyPlus/CurveManager.hh>
      57             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58             : #include <EnergyPlus/DataGenerators.hh>
      59             : #include <EnergyPlus/DataGlobalConstants.hh>
      60             : #include <EnergyPlus/DataHVACGlobals.hh>
      61             : #include <EnergyPlus/DataLoopNode.hh>
      62             : #include <EnergyPlus/GeneratorDynamicsManager.hh>
      63             : #include <EnergyPlus/MicroCHPElectricGenerator.hh>
      64             : #include <EnergyPlus/PlantUtilities.hh>
      65             : #include <EnergyPlus/ScheduleManager.hh>
      66             : 
      67             : namespace EnergyPlus {
      68             : 
      69             : namespace GeneratorDynamicsManager {
      70             : 
      71             :     //_______________________________________________
      72             :     // Utility modules used by other generators.
      73             :     //
      74             :     // GeneratorDynamicsManager
      75             :     //   reused among some generators to on/off state, transient limits, control implications etc.
      76             : 
      77             :     // Module containing the routines dealing with the management of dynamic constraints on Generator response
      78             : 
      79             :     // MODULE INFORMATION:
      80             :     //       AUTHOR        B. Griffith
      81             :     //       DATE WRITTEN   July 2006
      82             : 
      83             :     // PURPOSE OF THIS MODULE:
      84             :     // collect routines for managing generator states
      85             :     // reused by different generator models
      86             :     //  determine response that generator is capable of providing
      87             :     //  given load request data
      88             :     //   models requiring calculations across timesteps
      89             : 
      90             :     using namespace DataGenerators;
      91          10 :     void SetupGeneratorControlStateManager(EnergyPlusData &state, int const GenNum) // index of generator to setup
      92             :     {
      93             :         // SUBROUTINE INFORMATION:
      94             :         //       AUTHOR         B. Griffith
      95             :         //       DATE WRITTEN   July 2006
      96             : 
      97             :         // PURPOSE OF THIS SUBROUTINE:
      98             :         // sets up data structures
      99             : 
     100             :         // METHODOLOGY EMPLOYED:
     101             :         // like a get input routine but feeds from
     102             :         //  parent objects, could have its own input object someday
     103             : 
     104             :         // get the number of generators that might use this module
     105          10 :         int NumGensWDynamics = state.dataCHPElectGen->NumMicroCHPs; // TODO  + NumFuelCellCGenerators
     106             : 
     107          10 :         if (!allocated(state.dataGenerator->GeneratorDynamics)) {
     108           2 :             state.dataGenerator->GeneratorDynamics.allocate(NumGensWDynamics);
     109             :         }
     110             : 
     111             :         // first populate with Micro CHP data
     112             : 
     113          10 :         state.dataGenerator->GeneratorDynamics(GenNum).Name = state.dataCHPElectGen->MicroCHP(GenNum).Name;
     114          10 :         state.dataGenerator->GeneratorDynamics(GenNum).PelMin = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MinElecPower;
     115          10 :         state.dataGenerator->GeneratorDynamics(GenNum).PelMax = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MaxElecPower;
     116          10 :         state.dataGenerator->GeneratorDynamics(GenNum).UpTranLimit = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaPelMax;
     117          10 :         state.dataGenerator->GeneratorDynamics(GenNum).DownTranLimit = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaPelMax;
     118          10 :         state.dataGenerator->GeneratorDynamics(GenNum).UpTranLimitFuel = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaFuelMdotMax;
     119          10 :         state.dataGenerator->GeneratorDynamics(GenNum).DownTranLimitFuel = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaFuelMdotMax;
     120          10 :         state.dataGenerator->GeneratorDynamics(GenNum).WarmUpByTimeDelay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpByTimeDelay;
     121          10 :         state.dataGenerator->GeneratorDynamics(GenNum).WarmUpByEngineTemp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpByEngineTemp;
     122          10 :         state.dataGenerator->GeneratorDynamics(GenNum).MandatoryFullCoolDown = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MandatoryFullCoolDown;
     123          10 :         state.dataGenerator->GeneratorDynamics(GenNum).WarmRestartOkay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmRestartOkay;
     124          10 :         state.dataGenerator->GeneratorDynamics(GenNum).WarmUpDelay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpDelay;
     125          10 :         state.dataGenerator->GeneratorDynamics(GenNum).CoolDownDelay =
     126          10 :             state.dataCHPElectGen->MicroCHP(GenNum).A42Model.CoolDownDelay / DataGlobalConstants::SecInHour; // seconds to hours
     127          10 :         state.dataGenerator->GeneratorDynamics(GenNum).PcoolDown = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.PcoolDown;
     128          10 :         state.dataGenerator->GeneratorDynamics(GenNum).Pstandby = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.Pstandby;
     129          10 :         state.dataGenerator->GeneratorDynamics(GenNum).MCeng = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MCeng;
     130          10 :         state.dataGenerator->GeneratorDynamics(GenNum).MCcw = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MCcw;
     131          10 :         state.dataGenerator->GeneratorDynamics(GenNum).kf = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.kf;
     132          10 :         state.dataGenerator->GeneratorDynamics(GenNum).TnomEngOp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.TnomEngOp;
     133          10 :         state.dataGenerator->GeneratorDynamics(GenNum).kp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.kp;
     134          10 :         state.dataGenerator->GeneratorDynamics(GenNum).AvailabilitySchedID = state.dataCHPElectGen->MicroCHP(GenNum).AvailabilitySchedID;
     135          10 :         state.dataGenerator->GeneratorDynamics(GenNum).StartUpTimeDelay =
     136          10 :             state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpDelay / DataGlobalConstants::SecInHour; // seconds to hours
     137             : 
     138          10 :         state.dataGenerator->GeneratorDynamics(GenNum).ElectEffNom = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
     139          10 :         state.dataGenerator->GeneratorDynamics(GenNum).ThermEffNom = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff;
     140          30 :         state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMax = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff *
     141          20 :                                                                    state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MaxElecPower /
     142          10 :                                                                    state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
     143          30 :         state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMin = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff *
     144          20 :                                                                    state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MinElecPower /
     145          10 :                                                                    state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
     146          10 :         state.dataGenerator->GeneratorDynamics(GenNum).QdotHXOpt = state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMax;
     147          10 :         state.dataCHPElectGen->MicroCHP(GenNum).DynamicsControlID = GenNum;
     148          10 :     }
     149             : 
     150       13932 :     void ManageGeneratorControlState(EnergyPlusData &state,
     151             :                                      GeneratorType const GeneratorType,                 // type of Generator
     152             :                                      [[maybe_unused]] std::string const &GeneratorName, // user specified name of Generator
     153             :                                      int const GeneratorNum,                            // Generator number
     154             :                                      bool const RunFlagElectCenter,                 // TRUE when Generator operating per electric load center request
     155             :                                      bool const RunFlagPlant,                       // TRUE when generator operating per Plant request (always false)
     156             :                                      Real64 const ElecLoadRequest,                  // Generator Electrical power demand
     157             :                                      Real64 const ThermalLoadRequest,               // cogenerator Thermal power demand
     158             :                                      Real64 &ElecLoadProvided,                      // power allowed
     159             :                                      DataGenerators::OperatingMode &OperatingMode,  // operating mode
     160             :                                      Real64 &PLRforSubtimestepStartUp,              // part load ratio for switch to normal from start up
     161             :                                      Real64 &PLRforSubtimestepShutDown,             // part load ratio for switch from cool down to other
     162             :                                      [[maybe_unused]] bool const FirstHVACIteration // True is this is first HVAC iteration
     163             :     )
     164             :     {
     165             : 
     166             :         // SUBROUTINE INFORMATION:
     167             :         //       AUTHOR         B Griffith
     168             :         //       DATE WRITTEN   February-March 2007  (replaced July 2006 attempt)
     169             :         //       MODIFIED       Dec 2009, check and constrain with flow available from plant
     170             :         //       RE-ENGINEERED  na
     171             : 
     172             :         // PURPOSE OF THIS SUBROUTINE:
     173             :         // provide a service to other generators to make decisions, mostly temporal, or cross-timestep issues
     174             :         //  used to model internal controlling issues within an individual generator model
     175             :         //  This subroutine determines the current operating mode and returns the allowed power and
     176             :         // and part load ratio for certain sub-time step switching e.g. in and out of normal mode or cool down mode
     177             : 
     178             :         // METHODOLOGY EMPLOYED:
     179             :         // model controls-related issues, rules based algorithm
     180             :         // Control decision results include:
     181             :         //     -- electrical load allowed/resulting/provided
     182             :         //     -- new operating mode
     183             :         //     -- part load this timestep for shift to normal mode occuring midway in timestep
     184             :         //     -- part load this timestep for shift out of cool down mode
     185             : 
     186             :         // Input data used to make control decisions include:
     187             :         //     -- Electrical load request
     188             :         //     -- Thermal Load request
     189             :         //     -- RunFlagElectricCenter
     190             :         //     -- RunFlagPlant
     191             :         //     -- previous timestep operating mode
     192             :         //     -- previous timestep Power generated
     193             :         //     -- availability schedule (off if not available)
     194             :         //     -- Generator control parameter constants including
     195             :         //           ** Start Up Time Delay  (in hours)
     196             :         //           ** Cool-down time delay (in hours)
     197             :         //     -- Expected Plant flow rate
     198             :         //     -- minimum cooling water flow rate
     199             : 
     200             :         // Algorithm summary
     201             :         //   1.  examine calling run flags and refine electric load request to account for
     202             :         //       thermal load requests (not yet ready for prime time)
     203             :         //   2.  Determine states of various control inputs that change during simulation
     204             :         //   3.  enter case statement based on previous operating mode.
     205             :         //       --  decide on current operating mode
     206             :         //       --  calculate part loads
     207             : 
     208             :         //   4.  based on current operating mode determine allowed/provided electrical load
     209             :         //        a. set allowed elec load by mode
     210             :         //        b. set allowed elec load by constraints on rate of change
     211             :         //        c. set allowed elec load by min and max
     212             : 
     213             :         //   5.  Calculated part load ratios for special cases.
     214             :         // REFERENCES:
     215             :         // controls specifications in Annex 42 model specs.
     216             :         // Using/Aliasing
     217             :         using namespace DataGlobalConstants;
     218       13932 :         auto &SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
     219       13932 :         auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
     220             :         using ScheduleManager::GetCurrentScheduleValue;
     221             :         using ScheduleManager::GetScheduleIndex;
     222             : 
     223             :         // Locals
     224             :         // SUBROUTINE ARGUMENT DEFINITIONS:
     225             :         // this is the part in normal mode
     226             :         // this is the part in cool down mode.
     227             : 
     228             :         // SUBROUTINE PARAMETER DEFINITIONS:
     229             :         // na
     230             : 
     231             :         // INTERFACE BLOCK SPECIFICATIONS:
     232             :         // na
     233             : 
     234             :         // DERIVED TYPE DEFINITIONS:
     235             :         // na
     236             : 
     237             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     238             :         bool RunFlag;                           // true if generator supposed to run
     239       13932 :         int DynaCntrlNum(0);                    // index in GeneratorDynamics structure for this generator          ! na
     240             :         Real64 CurrentFractionalDay;            // working var, time in decimal days
     241             :         Real64 EndingFractionalDay;             // working var, time is decimal days
     242             :         Real64 LastSystemTimeStepFractionalDay; // working var, time is decimal days
     243             :         Real64 MaxPel;                          // working variable for max allowed by transient constraint
     244             :         Real64 MinPel;                          // working variabel for min allowed by transient constraint
     245             :         Real64 PelInput;                        // holds initial value of IN var
     246             :         Real64 Pel;
     247       13932 :         DataGenerators::OperatingMode newOpMode(DataGenerators::OperatingMode::Invalid);
     248             :         Real64 SchedVal;
     249             :         Real64 ElectLoadForThermalRequest;
     250             :         bool ConstrainedMaxP;           // true if request was altered because of max power limit
     251             :         bool ConstrainedMinP;           // true if request was altered because of min power limit
     252             :         bool ConstrainedIncreasingPdot; // true if request was altered because of power rate of change up
     253             :         bool ConstrainedDecreasingPdot; // true if request was altered because of power rate of change down
     254             :         bool ConstrainedByPlant;        // true if request was altered because of cooling water problem
     255             :         bool PLRStartUp;                // true if subtimestep issue involving startup
     256             :         bool PLRShutDown;
     257       13932 :         auto &InletCWnode = state.dataGenerator->InletCWnode;
     258       13932 :         auto &InternalFlowControl = state.dataGenerator->InternalFlowControl;
     259       13932 :         auto &TcwIn = state.dataGenerator->TcwIn;
     260       13932 :         auto &TrialMdotcw = state.dataGenerator->TrialMdotcw;
     261       13932 :         auto &LimitMinMdotcw = state.dataGenerator->LimitMinMdotcw;
     262             : 
     263             :         // inits
     264       13932 :         PLRforSubtimestepStartUp = 1.0;
     265       13932 :         PLRforSubtimestepShutDown = 0.0;
     266       13932 :         ConstrainedMaxP = false;
     267       13932 :         ConstrainedMinP = false;
     268       13932 :         ConstrainedIncreasingPdot = false;
     269       13932 :         ConstrainedDecreasingPdot = false;
     270       13932 :         ConstrainedByPlant = false;
     271       13932 :         PLRStartUp = false;
     272       13932 :         PLRShutDown = false;
     273       13932 :         InternalFlowControl = false;
     274             : 
     275             :         // get index for this generator in dynamics control structure
     276       13932 :         switch (GeneratorType) {
     277       13932 :         case GeneratorType::MicroCHP: {
     278       13932 :             DynaCntrlNum = state.dataCHPElectGen->MicroCHP(GeneratorNum).DynamicsControlID;
     279             :             // OutletCWnode = MicroCHPElectricGenerator::MicroCHP(GeneratorNum)%PlantOutletNodeID
     280       13932 :             InletCWnode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID;
     281       13932 :             TcwIn = state.dataLoopNodes->Node(state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID).Temp;
     282       13932 :             if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.InternalFlowControl) {
     283        7798 :                 InternalFlowControl = true;
     284             :             }
     285       13932 :             LimitMinMdotcw = state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.MinWaterMdot;
     286       13932 :         } break;
     287           0 :         case GeneratorType::FuelCell: {
     288             :             // not yet
     289           0 :         } break;
     290           0 :         default:
     291           0 :             break;
     292             :         }
     293             : 
     294       13932 :         PelInput = ElecLoadRequest;
     295       13932 :         ElectLoadForThermalRequest = 0.0;
     296       13932 :         if ((ThermalLoadRequest > 0.0) && RunFlagPlant) { // deal with possible thermal load following
     297             :             // Modify electric load request based on thermal load following signal using nominal efficiencies
     298           0 :             ElectLoadForThermalRequest = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).ThermEffNom * ThermalLoadRequest /
     299           0 :                                          state.dataGenerator->GeneratorDynamics(DynaCntrlNum).ElectEffNom;
     300           0 :             PelInput = max(PelInput, ElectLoadForThermalRequest);
     301             :         }
     302             : 
     303       13932 :         if ((RunFlagElectCenter) || (RunFlagPlant)) {
     304        4335 :             RunFlag = true;
     305             :         } else {
     306        9597 :             RunFlag = false;
     307             :         }
     308             : 
     309             :         // check availability schedule
     310       13932 :         SchedVal = GetCurrentScheduleValue(state, state.dataGenerator->GeneratorDynamics(DynaCntrlNum).AvailabilitySchedID);
     311             : 
     312       13932 :         Pel = PelInput;
     313             : 
     314             :         // get data to check if sufficient flow available from Plant
     315       13932 :         if (InternalFlowControl && (SchedVal > 0.0)) {
     316        7798 :             TrialMdotcw = FuncDetermineCWMdotForInternalFlowControl(state, GeneratorNum, Pel, TcwIn);
     317             :         } else {
     318        6134 :             TrialMdotcw = state.dataLoopNodes->Node(InletCWnode).MassFlowRate;
     319             :         }
     320             : 
     321             :         // determine current operating mode.
     322       13932 :         switch (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).LastOpMode) {
     323        9870 :         case DataGenerators::OperatingMode::Off:
     324             :         case DataGenerators::OperatingMode::Standby: {
     325             :             // possible future states {Off, Standby, WarmUp,Normal }
     326        9870 :             if (SchedVal == 0.0) {
     327        5588 :                 newOpMode = DataGenerators::OperatingMode::Off;
     328             : 
     329        4282 :             } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
     330        3017 :                 newOpMode = DataGenerators::OperatingMode::Standby;
     331        1265 :             } else if ((SchedVal != 0.0) && (RunFlag)) {
     332             : 
     333        1265 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
     334             : 
     335        1265 :                     if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
     336          42 :                         newOpMode = DataGenerators::OperatingMode::Normal;
     337             : 
     338             :                         // is startUp time delay longer than timestep?
     339        1223 :                     } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay >= TimeStepSys) {
     340           0 :                         newOpMode = DataGenerators::OperatingMode::WarmUp;
     341             :                         // generator just started so set start time
     342           0 :                         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
     343           0 :                             double(state.dataGlobal->DayOfSim) +
     344           0 :                             (int(state.dataGlobal->CurrentTime) +
     345           0 :                              (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
     346             :                                 DataGlobalConstants::HoursInDay;
     347             : 
     348             :                     } else { // warm up period is less than a single system time step
     349        1223 :                         newOpMode = DataGenerators::OperatingMode::Normal;
     350        1223 :                         PLRStartUp = true;
     351        1223 :                         PLRforSubtimestepStartUp =
     352        1223 :                             (TimeStepSys - state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay) / TimeStepSys;
     353             :                     }
     354             :                 }
     355        1265 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByEngineTemp) {
     356           0 :                     if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng >=
     357           0 :                         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) {
     358           0 :                         newOpMode = DataGenerators::OperatingMode::Normal;
     359             :                         // assume linear interpolation for PLR
     360           0 :                         PLRStartUp = true;
     361           0 :                         if ((state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     362           0 :                              state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast) > 0.0) {
     363             :                             // protect divide by zero or neg
     364           0 :                             PLRforSubtimestepStartUp = (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     365           0 :                                                         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) /
     366           0 :                                                        (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     367           0 :                                                         state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast);
     368             :                         } else {
     369           0 :                             PLRforSubtimestepStartUp = 1.0;
     370             :                         }
     371             :                     } else {
     372           0 :                         newOpMode = DataGenerators::OperatingMode::WarmUp;
     373             :                     }
     374             :                 }
     375             :             }
     376             : 
     377        9870 :         } break;
     378           0 :         case DataGenerators::OperatingMode::WarmUp: {
     379             :             // possible Future states {OFF, WarmUp, Normal, CoolDown }
     380             :             // check availability manager
     381           0 :             if (SchedVal == 0.0) {
     382             :                 // to off unless cool down time period is needed
     383           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
     384           0 :                     newOpMode = DataGenerators::OperatingMode::Off;
     385             :                 } else {
     386           0 :                     if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > TimeStepSys) {
     387           0 :                         newOpMode = DataGenerators::OperatingMode::CoolDown;
     388             :                         // need to reset time of last shut down here
     389           0 :                         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
     390           0 :                             double(state.dataGlobal->DayOfSim) +
     391           0 :                             (int(state.dataGlobal->CurrentTime) +
     392           0 :                              (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     393             :                                 DataGlobalConstants::HoursInDay;
     394             :                     } else {
     395           0 :                         newOpMode = DataGenerators::OperatingMode::Off;
     396             :                     }
     397             :                 }
     398           0 :             } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
     399             :                 // to standby unless cool down time period is needed
     400           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
     401           0 :                     newOpMode = DataGenerators::OperatingMode::Standby;
     402             :                 } else {
     403           0 :                     if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > TimeStepSys) {
     404           0 :                         newOpMode = DataGenerators::OperatingMode::CoolDown;
     405             :                         // need to reset time of last shut down here
     406           0 :                         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
     407           0 :                             double(state.dataGlobal->DayOfSim) +
     408           0 :                             (int(state.dataGlobal->CurrentTime) +
     409           0 :                              (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     410             :                                 DataGlobalConstants::HoursInDay;
     411             : 
     412             :                     } else {
     413           0 :                         newOpMode = DataGenerators::OperatingMode::Standby;
     414             :                         // assuming no PLR situation unless engine made to normal operation.
     415             :                     }
     416             :                 }
     417           0 :             } else if ((SchedVal != 0.0) && (RunFlag)) {
     418             :                 // either warm up or normal
     419             :                 // check if warm up completed, depends on type of warm up control time delay or reach nominal temperature
     420           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
     421             :                     // compare current time to when warm up is over
     422             :                     // calculate time for end of warmup period
     423           0 :                     CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
     424           0 :                                            (int(state.dataGlobal->CurrentTime) +
     425           0 :                                             (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     426             :                                                DataGlobalConstants::HoursInDay;
     427           0 :                     EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp +
     428           0 :                                           state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay / DataGlobalConstants::HoursInDay;
     429           0 :                     if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) || (CurrentFractionalDay > EndingFractionalDay)) {
     430           0 :                         newOpMode = DataGenerators::OperatingMode::Normal;
     431           0 :                         PLRStartUp = true;
     432           0 :                         LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
     433           0 :                         PLRforSubtimestepStartUp =
     434           0 :                             ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
     435             :                     } else {
     436           0 :                         newOpMode = DataGenerators::OperatingMode::WarmUp;
     437             :                     }
     438             : 
     439           0 :                 } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByEngineTemp) {
     440           0 :                     if (GeneratorType == GeneratorType::MicroCHP) {
     441             :                         // only change to normal if this is result from completed timestep, not just an interation
     442           0 :                         if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast >=
     443           0 :                             state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) {
     444           0 :                             newOpMode = DataGenerators::OperatingMode::Normal;
     445             :                             // assume linear interpolation for PLR
     446           0 :                             PLRStartUp = true;
     447           0 :                             if ((state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     448           0 :                                  state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast) > 0.0) {
     449             :                                 // protect divide by zero or neg
     450           0 :                                 PLRforSubtimestepStartUp = (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     451           0 :                                                             state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) /
     452           0 :                                                            (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
     453           0 :                                                             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast);
     454             :                             } else {
     455           0 :                                 PLRforSubtimestepStartUp = 1.0;
     456             :                             }
     457             :                         } else {
     458           0 :                             newOpMode = DataGenerators::OperatingMode::WarmUp;
     459             :                         }
     460             :                     }
     461             :                 } else {
     462             :                     // shouldn't come here
     463             :                     // Write(*,*) 'problem with warm up type of control logical flags'
     464             :                 }
     465             :             }
     466           0 :         } break;
     467        4062 :         case DataGenerators::OperatingMode::Normal: {
     468             :             // possible Future states {CoolDown, standby, off}
     469        4062 :             if (((SchedVal == 0.0) || (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
     470             :                 // is cool down time delay longer than timestep?
     471        1984 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
     472          42 :                     if (SchedVal != 0.0) {
     473           0 :                         newOpMode = DataGenerators::OperatingMode::Standby;
     474             :                     } else {
     475          42 :                         newOpMode = DataGenerators::OperatingMode::Off;
     476             :                     }
     477         950 :                 } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay >= TimeStepSys) {
     478           0 :                     newOpMode = DataGenerators::OperatingMode::CoolDown;
     479             :                     // also, generator just shut down so record shut down time
     480           0 :                     state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
     481           0 :                         double(state.dataGlobal->DayOfSim) +
     482           0 :                         (int(state.dataGlobal->CurrentTime) +
     483           0 :                          (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     484             :                             DataGlobalConstants::HoursInDay;
     485             :                 } else { // cool down period is less than a single system time step
     486         950 :                     if (SchedVal != 0.0) {
     487         950 :                         newOpMode = DataGenerators::OperatingMode::Standby;
     488             :                     } else {
     489           0 :                         newOpMode = DataGenerators::OperatingMode::Off;
     490             :                     }
     491         950 :                     PLRShutDown = true;
     492         950 :                     PLRforSubtimestepShutDown = (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay) / TimeStepSys;
     493             : 
     494             :                     // also, generator just shut down so record shut down time
     495         950 :                     state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
     496        1900 :                         double(state.dataGlobal->DayOfSim) +
     497        1900 :                         (int(state.dataGlobal->CurrentTime) +
     498        1900 :                          (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     499             :                             DataGlobalConstants::HoursInDay;
     500             :                 }
     501        3070 :             } else if ((SchedVal != 0.0) && (RunFlag)) {
     502             : 
     503        3070 :                 newOpMode = DataGenerators::OperatingMode::Normal;
     504             :             }
     505        4062 :         } break;
     506           0 :         case DataGenerators::OperatingMode::CoolDown: {
     507             :             // possible Future States {Standby, OFF, WarmUp, Normal}
     508             : 
     509           0 :             if (SchedVal == 0.0) { // no longer available.
     510             :                 // probably goes to off but could be stuck in cool down for awhile
     511           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
     512             :                     // calculate time for end of cool down period
     513           0 :                     CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
     514           0 :                                            (int(state.dataGlobal->CurrentTime) +
     515           0 :                                             (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     516             :                                                DataGlobalConstants::HoursInDay;
     517           0 :                     EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
     518           0 :                                           state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
     519           0 :                                           (TimeStepSys / DataGlobalConstants::HoursInDay);
     520           0 :                     if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
     521             :                         (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
     522           0 :                         newOpMode = DataGenerators::OperatingMode::Off;
     523           0 :                         PLRShutDown = true;
     524           0 :                         LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
     525           0 :                         PLRforSubtimestepShutDown =
     526           0 :                             (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
     527             :                     } else { // CurrentFractionalDay > EndingFractionalDay
     528           0 :                         newOpMode = DataGenerators::OperatingMode::CoolDown;
     529             :                     }
     530             :                 } else {
     531           0 :                     newOpMode = DataGenerators::OperatingMode::Off;
     532             :                 }
     533           0 :             } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
     534             :                 // probably goes to standby but could be stuck in cool down for awhile
     535           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
     536             :                     // calculate time for end of cool down period
     537           0 :                     CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
     538           0 :                                            (int(state.dataGlobal->CurrentTime) +
     539           0 :                                             (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     540             :                                                DataGlobalConstants::HoursInDay;
     541           0 :                     EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
     542           0 :                                           state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
     543           0 :                                           (TimeStepSys / DataGlobalConstants::HoursInDay);
     544           0 :                     if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
     545             :                         (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
     546           0 :                         newOpMode = DataGenerators::OperatingMode::Standby;
     547           0 :                         PLRShutDown = true;
     548           0 :                         LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
     549           0 :                         PLRforSubtimestepShutDown =
     550           0 :                             (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
     551             :                     } else { // CurrentFractionalDay < EndingFractionalDay
     552           0 :                         newOpMode = DataGenerators::OperatingMode::CoolDown;
     553             :                     }
     554             :                 } else {
     555           0 :                     newOpMode = DataGenerators::OperatingMode::Standby;
     556             :                 }
     557           0 :             } else if ((SchedVal != 0.0) && (RunFlag)) {
     558             :                 // was in cool down mode but is now being asked to restart
     559             :                 // probably goes to warm up but could be stuck in cool down or jump to normal
     560           0 :                 if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).MandatoryFullCoolDown) {
     561             :                     // is cool down done or not?
     562           0 :                     if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
     563             :                         // calculate time for end of cool down period
     564           0 :                         CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
     565           0 :                                                (int(state.dataGlobal->CurrentTime) +
     566           0 :                                                 (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     567             :                                                    DataGlobalConstants::HoursInDay;
     568           0 :                         EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
     569           0 :                                               state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
     570           0 :                                               (TimeStepSys / DataGlobalConstants::HoursInDay);
     571           0 :                         if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
     572             :                             (CurrentFractionalDay < EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
     573             : 
     574           0 :                             newOpMode = DataGenerators::OperatingMode::CoolDown;
     575             :                         } else { // CurrentFractionalDay > EndingFractionalDay
     576             :                             // could go to warm up or normal now
     577           0 :                             PLRShutDown = true;
     578           0 :                             LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
     579           0 :                             PLRforSubtimestepShutDown =
     580           0 :                                 (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
     581           0 :                             if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
     582           0 :                                 newOpMode = DataGenerators::OperatingMode::Normal;
     583             :                                 // possible PLR on start up.
     584           0 :                                 PLRStartUp = true;
     585           0 :                                 PLRforSubtimestepStartUp =
     586           0 :                                     ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
     587             : 
     588           0 :                             } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay > 0.0) {
     589             :                                 // is remaining time enough?
     590           0 :                                 if ((CurrentFractionalDay - EndingFractionalDay) >
     591           0 :                                     state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay) {
     592           0 :                                     newOpMode = DataGenerators::OperatingMode::Normal;
     593             :                                     // possible PLR on start up.
     594           0 :                                     PLRStartUp = true;
     595           0 :                                     PLRforSubtimestepStartUp =
     596           0 :                                         ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
     597             :                                 } else {
     598           0 :                                     newOpMode = DataGenerators::OperatingMode::WarmUp;
     599             :                                     // generator just started so set start time
     600           0 :                                     state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
     601           0 :                                         double(state.dataGlobal->DayOfSim) +
     602           0 :                                         (int(state.dataGlobal->CurrentTime) +
     603           0 :                                          (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
     604             :                                             DataGlobalConstants::HoursInDay;
     605             :                                 }
     606             :                             }
     607             :                         }
     608             :                     } else {
     609             : 
     610           0 :                         newOpMode = DataGenerators::OperatingMode::Standby;
     611             :                     }
     612             :                 } else { // not mandetory cool donw
     613             :                     // likely to go into warm up but if no warm up then back to normal
     614           0 :                     if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
     615           0 :                         if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
     616           0 :                             newOpMode = DataGenerators::OperatingMode::Normal;
     617             : 
     618           0 :                         } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay > 0.0) {
     619           0 :                             CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
     620           0 :                                                    (int(state.dataGlobal->CurrentTime) +
     621           0 :                                                     (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
     622             :                                                        DataGlobalConstants::HoursInDay;
     623           0 :                             EndingFractionalDay =
     624           0 :                                 state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
     625           0 :                                 state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay;
     626           0 :                             if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
     627             :                                 (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
     628           0 :                                 newOpMode = DataGenerators::OperatingMode::Normal;
     629             :                                 // possible PLR on start up.
     630           0 :                                 PLRStartUp = true;
     631           0 :                                 LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
     632           0 :                                 PLRforSubtimestepStartUp =
     633           0 :                                     ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
     634             :                             } else {
     635           0 :                                 newOpMode = DataGenerators::OperatingMode::WarmUp;
     636             :                                 // set start up time
     637             :                                 // generator just started so set start time
     638           0 :                                 state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
     639           0 :                                     double(state.dataGlobal->DayOfSim) +
     640           0 :                                     (int(state.dataGlobal->CurrentTime) +
     641           0 :                                      (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
     642             :                                         DataGlobalConstants::HoursInDay;
     643             :                             }
     644             :                         }
     645             :                     }
     646             :                 }
     647             :             }
     648           0 :         } break;
     649           0 :         default:
     650           0 :             break;
     651             :         } // previous case
     652             : 
     653       13932 :         if (PLRforSubtimestepStartUp < 0.0) PLRforSubtimestepStartUp = 0.0;
     654       13932 :         if (PLRforSubtimestepStartUp > 1.0) PLRforSubtimestepStartUp = 1.0;
     655             : 
     656       13932 :         if (PLRforSubtimestepShutDown < 0.0) PLRforSubtimestepShutDown = 0.0;
     657       13932 :         if (PLRforSubtimestepShutDown > 1.0) PLRforSubtimestepShutDown = 1.0;
     658             : 
     659       13932 :         if (newOpMode == DataGenerators::OperatingMode::WarmUp) {
     660           0 :             switch (GeneratorType) {
     661           0 :             case GeneratorType::FuelCell: {
     662             :                 // constant power level during start up (modeling artifact)
     663             :                 //? hours or seconds here?
     664           0 :                 Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpElectProd /
     665           0 :                       state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay;
     666             : 
     667           0 :             } break;
     668           0 :             case GeneratorType::MicroCHP: {
     669           0 :                 Pel = PelInput * PLRforSubtimestepStartUp;
     670           0 :             } break;
     671           0 :             default:
     672           0 :                 break;
     673             :             }
     674             :         }
     675             : 
     676       13932 :         if (newOpMode == DataGenerators::OperatingMode::Normal) {
     677             :             // correct if switched to normal at sub timestep
     678        4335 :             Pel *= PLRforSubtimestepStartUp;
     679             :             // unit may have constraints from transient limits or operating ranges.
     680        4335 :             if (Pel > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep) { // powering up
     681        4958 :                 MaxPel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep +
     682        2479 :                          state.dataGenerator->GeneratorDynamics(DynaCntrlNum).UpTranLimit * TimeStepSys * DataGlobalConstants::SecInHour;
     683        2479 :                 if (MaxPel < Pel) {
     684           0 :                     Pel = MaxPel;
     685             :                 }
     686        1856 :             } else if (Pel < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep) { // powering down
     687        1116 :                 MinPel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep -
     688         558 :                          state.dataGenerator->GeneratorDynamics(DynaCntrlNum).DownTranLimit * TimeStepSys * DataGlobalConstants::SecInHour;
     689         558 :                 if (Pel < MinPel) {
     690           0 :                     Pel = MinPel;
     691             :                 }
     692             :             }
     693             :         }
     694             : 
     695       13932 :         if (newOpMode == DataGenerators::OperatingMode::CoolDown) {
     696           0 :             Pel = 0.0; // assumes no power generated during shut down
     697             :         }
     698             : 
     699       13932 :         if (newOpMode == DataGenerators::OperatingMode::Off) {
     700        5630 :             Pel = 0.0; // assumes no power generated during OFF mode
     701             :         }
     702             : 
     703       13932 :         if (newOpMode == DataGenerators::OperatingMode::Standby) {
     704        3967 :             Pel = 0.0; // assumes no power generated during standby mode
     705             :         }
     706             : 
     707             :         // Control step 3: adjust for max and min limits on Pel
     708             : 
     709       13932 :         if (Pel < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMin) {
     710           0 :             Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMin;
     711             :         }
     712       13932 :         if (Pel > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMax) {
     713           0 :             Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMax;
     714             :         }
     715             : 
     716             :         // now do record keeping for amount of time spent in various operating modes
     717       13932 :         switch (GeneratorType) {
     718       13932 :         case GeneratorType::MicroCHP: {
     719             :             // first clear out values
     720       13932 :             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = 0.0;
     721       13932 :             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = 0.0;
     722       13932 :             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = 0.0;
     723       13932 :             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = 0.0;
     724       13932 :             state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime = 0.0;
     725       13932 :             switch (newOpMode) {
     726        5630 :             case DataGenerators::OperatingMode::Off: {
     727        5630 :                 if (PLRforSubtimestepShutDown == 0.0) {
     728        5630 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     729           0 :                 } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
     730           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
     731           0 :                         TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
     732           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime =
     733           0 :                         TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
     734             :                 } else {
     735           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     736             :                 }
     737        5630 :             } break;
     738        3967 :             case DataGenerators::OperatingMode::Standby: {
     739        3967 :                 if (PLRforSubtimestepShutDown == 0.0) {
     740        3017 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     741         950 :                 } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
     742         950 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
     743         950 :                         TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
     744         950 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime =
     745         950 :                         TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
     746             :                 } else {
     747           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     748             :                 }
     749        3967 :             } break;
     750           0 :             case DataGenerators::OperatingMode::WarmUp: {
     751           0 :                 if (PLRforSubtimestepShutDown == 0.0) {
     752           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     753           0 :                 } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
     754           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
     755           0 :                         TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
     756           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime =
     757           0 :                         TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
     758             :                 } else {
     759           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     760             :                 }
     761           0 :             } break;
     762        4335 :             case DataGenerators::OperatingMode::Normal: {
     763        4335 :                 if (PLRforSubtimestepStartUp == 0.0) {
     764           0 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     765             : 
     766        4335 :                 } else if ((PLRforSubtimestepStartUp > 0.0) && (PLRforSubtimestepStartUp < 1.0)) {
     767        1223 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime =
     768        1223 :                         TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepStartUp);
     769        1223 :                     state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime =
     770        1223 :                         TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepStartUp);
     771             :                 } else {
     772        3112 :                     if (PLRforSubtimestepShutDown == 0.0) {
     773        3112 :                         state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     774           0 :                     } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
     775           0 :                         state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
     776           0 :                             TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
     777           0 :                         state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime =
     778           0 :                             TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
     779             :                     } else {
     780           0 :                         state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     781             :                     }
     782             :                 }
     783        4335 :             } break;
     784           0 :             case DataGenerators::OperatingMode::CoolDown: {
     785           0 :                 state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
     786           0 :             } break;
     787           0 :             default:
     788           0 :                 break;
     789             :             }
     790       13932 :         } break;
     791           0 :         case GeneratorType::FuelCell: {
     792             :             // not yet using this control manager
     793           0 :         } break;
     794           0 :         default:
     795           0 :             break;
     796             :         }
     797             : 
     798       13932 :         ElecLoadProvided = Pel;
     799             : 
     800       13932 :         state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CurrentOpMode = newOpMode;
     801       13932 :         OperatingMode = newOpMode;
     802       13932 :     }
     803             : 
     804        4335 :     void ManageGeneratorFuelFlow(EnergyPlusData &state,
     805             :                                  GeneratorType const GeneratorType,                 // type of Generator
     806             :                                  [[maybe_unused]] std::string const &GeneratorName, // user specified name of Generator
     807             :                                  int const GeneratorNum,                            // Generator number
     808             :                                  [[maybe_unused]] bool const RunFlag,               // TRUE when Generator operating
     809             :                                  Real64 const FuelFlowRequest,                      // Generator demand mdot kg/ s
     810             :                                  Real64 &FuelFlowProvided,                          // allowed after constraints kg/s
     811             :                                  bool &ConstrainedIncreasingMdot,                   // true if request was altered because of fuel rate of change up
     812             :                                  bool &ConstrainedDecreasingMdot                    // true if request was altered because of fuel rate of change down
     813             :     )
     814             :     {
     815             : 
     816             :         // SUBROUTINE INFORMATION:
     817             :         //       AUTHOR         B. Griffith
     818             :         //       DATE WRITTEN   July 2006
     819             :         //       MODIFIED       na
     820             :         //       RE-ENGINEERED  na
     821             : 
     822             :         // PURPOSE OF THIS SUBROUTINE:
     823             :         // check if change in fuel flow rate is okay
     824             : 
     825             :         // Using/Aliasing
     826             :         using namespace DataGlobalConstants;
     827        4335 :         auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
     828             : 
     829             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     830             :         Real64 MdotFuel;
     831             :         Real64 MaxMdot;
     832             :         Real64 MinMdot;
     833        4335 :         int DynaCntrlNum(0);
     834             : 
     835        4335 :         ConstrainedIncreasingMdot = false;
     836        4335 :         ConstrainedDecreasingMdot = false;
     837        4335 :         MdotFuel = FuelFlowRequest;
     838             : 
     839             :         // get index from GeneratorNum
     840        4335 :         switch (GeneratorType) {
     841        4335 :         case GeneratorType::MicroCHP: {
     842        4335 :             DynaCntrlNum = state.dataCHPElectGen->MicroCHP(GeneratorNum).DynamicsControlID;
     843        4335 :         } break;
     844           0 :         default:
     845           0 :             break;
     846             :         }
     847             : 
     848        4335 :         if (FuelFlowRequest > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep) { // fuel flow is up
     849        2926 :             MaxMdot = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep +
     850        1463 :                       state.dataGenerator->GeneratorDynamics(DynaCntrlNum).UpTranLimitFuel * TimeStepSys * DataGlobalConstants::SecInHour;
     851        1463 :             if (MaxMdot < FuelFlowRequest) {
     852           0 :                 MdotFuel = MaxMdot;
     853           0 :                 ConstrainedIncreasingMdot = true;
     854             :             }
     855             : 
     856        2872 :         } else if (FuelFlowRequest < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep) { // fuel flow is down
     857        1124 :             MinMdot = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep -
     858         562 :                       state.dataGenerator->GeneratorDynamics(DynaCntrlNum).DownTranLimitFuel * TimeStepSys * DataGlobalConstants::SecInHour;
     859         562 :             if (FuelFlowRequest < MinMdot) {
     860           0 :                 MdotFuel = MinMdot;
     861           0 :                 ConstrainedDecreasingMdot = true;
     862             :             }
     863             :         } else {
     864             :             // do nothing
     865             :         }
     866             : 
     867        4335 :         FuelFlowProvided = MdotFuel;
     868        4335 :     }
     869             : 
     870       11629 :     Real64 FuncDetermineCWMdotForInternalFlowControl(EnergyPlusData &state,
     871             :                                                      int const GeneratorNum, // ID of generator
     872             :                                                      Real64 const Pnetss,    // power net steady state
     873             :                                                      Real64 const TcwIn      // temperature of cooling water at inlet
     874             :     )
     875             :     {
     876             : 
     877             :         // FUNCTION INFORMATION:
     878             :         //       AUTHOR         B. Griffith
     879             :         //       DATE WRITTEN   Dec 2009
     880             :         //       MODIFIED       na
     881             :         //       RE-ENGINEERED  B. Griffith, Sept 2010, plant upgrade
     882             : 
     883             :         // PURPOSE OF THIS FUNCTION:
     884             :         // common place to figure flow rates with internal flow control
     885             : 
     886             :         // METHODOLOGY EMPLOYED:
     887             :         // apply contraints imposed by plant according to flow lock, first HVAC iteration etc.
     888             : 
     889             :         // REFERENCES:
     890             :         // na
     891             : 
     892             :         // Using/Aliasing
     893             :         using Curve::CurveValue;
     894             :         using PlantUtilities::SetComponentFlowRate;
     895             : 
     896             :         // Return value
     897             :         Real64 FuncDetermineCWMdotForInternalFlowControl;
     898             : 
     899             :         // Locals
     900             :         // FUNCTION ARGUMENT DEFINITIONS:
     901             : 
     902             :         // FUNCTION PARAMETER DEFINITIONS:
     903             :         // na
     904             : 
     905             :         // INTERFACE BLOCK SPECIFICATIONS:
     906             :         // na
     907             : 
     908             :         // DERIVED TYPE DEFINITIONS:
     909             :         // na
     910             : 
     911             :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
     912             :         Real64 MdotCW;
     913             :         int InletNode;
     914             :         int OutletNode;
     915             : 
     916       11629 :         InletNode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID;
     917       11629 :         OutletNode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantOutletNodeID;
     918             : 
     919             :         // first evaluate curve
     920       11629 :         MdotCW = CurveValue(state, state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WaterFlowCurveID, Pnetss, TcwIn);
     921             : 
     922             :         // now apply constraints
     923       11629 :         MdotCW = max(0.0, MdotCW);
     924             : 
     925             :         // make sure plant can provide, utility call may change flow
     926       11629 :         if (state.dataCHPElectGen->MicroCHP(GeneratorNum).CWPlantLoc.loopNum > 0) { // protect early calls
     927       11629 :             SetComponentFlowRate(state, MdotCW, InletNode, OutletNode, state.dataCHPElectGen->MicroCHP(GeneratorNum).CWPlantLoc);
     928             :         }
     929             : 
     930       11629 :         FuncDetermineCWMdotForInternalFlowControl = MdotCW;
     931       11629 :         return FuncDetermineCWMdotForInternalFlowControl;
     932             :     }
     933             : 
     934             : } // namespace GeneratorDynamicsManager
     935             : 
     936        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13