LCOV - code coverage report
Current view: top level - EnergyPlus - ElectricPowerServiceManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 47.1 % 2850 1341
Test Date: 2025-06-02 12:03:30 Functions: 68.0 % 75 51

            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              : // C++ Headers
      49              : #include <memory>
      50              : #include <vector>
      51              : 
      52              : // ObjexxFCL Headers
      53              : #include <ObjexxFCL/Array1.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/CTElectricGenerator.hh>
      57              : #include <EnergyPlus/CurveManager.hh>
      58              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59              : #include <EnergyPlus/DataEnvironment.hh>
      60              : #include <EnergyPlus/DataGlobalConstants.hh>
      61              : #include <EnergyPlus/DataHVACGlobals.hh>
      62              : #include <EnergyPlus/DataHeatBalance.hh>
      63              : #include <EnergyPlus/DataIPShortCuts.hh>
      64              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      65              : #include <EnergyPlus/EMSManager.hh>
      66              : #include <EnergyPlus/ElectricPowerServiceManager.hh>
      67              : #include <EnergyPlus/FuelCellElectricGenerator.hh>
      68              : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
      69              : #include <EnergyPlus/ICEngineElectricGenerator.hh>
      70              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      71              : #include <EnergyPlus/MicroCHPElectricGenerator.hh>
      72              : #include <EnergyPlus/MicroturbineElectricGenerator.hh>
      73              : #include <EnergyPlus/OutputProcessor.hh>
      74              : #include <EnergyPlus/OutputReportPredefined.hh>
      75              : #include <EnergyPlus/PVWatts.hh>
      76              : #include <EnergyPlus/Photovoltaics.hh>
      77              : #include <EnergyPlus/Plant/DataPlant.hh>
      78              : #include <EnergyPlus/Plant/PlantLocation.hh>
      79              : #include <EnergyPlus/PlantUtilities.hh>
      80              : #include <EnergyPlus/ScheduleManager.hh>
      81              : #include <EnergyPlus/UtilityRoutines.hh>
      82              : #include <EnergyPlus/WindTurbine.hh>
      83              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      84              : 
      85              : #ifdef DEBUG_ARITHM_GCC_OR_CLANG
      86              : #    include <EnergyPlus/fenv_missing.h>
      87              : #endif
      88              : 
      89              : #ifdef DEBUG_ARITHM_MSVC
      90              : #    include <cfloat>
      91              : #endif
      92              : 
      93              : namespace EnergyPlus {
      94              : 
      95          144 : void createFacilityElectricPowerServiceObject(const EnergyPlusData &state)
      96              : {
      97          144 :     state.dataElectPwrSvcMgr->facilityElectricServiceObj = std::make_unique<ElectricPowerServiceManager>();
      98          144 : }
      99              : 
     100       249958 : void initializeElectricPowerServiceZoneGains(const EnergyPlusData &state) // namespace routine for handling call from InternalHeatGains
     101              : {
     102              :     // internal zone gains need to be re initialized for begin new environment earlier than the main call into manage electric power service
     103       249958 :     if (state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag && state.dataGlobal->BeginEnvrnFlag) {
     104          483 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->reinitZoneGainsAtBeginEnvironment();
     105          483 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag = false;
     106              :     }
     107       249958 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     108       249475 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag = true;
     109              :     }
     110       249958 : }
     111              : 
     112      1009294 : void ElectricPowerServiceManager::manageElectricPowerService(
     113              :     EnergyPlusData &state,
     114              :     bool const firstHVACIteration,
     115              :     bool &SimElecCircuits,      // simulation convergence flag
     116              :     bool const UpdateMetersOnly // if true then don't resimulate generators, just update meters.
     117              : )
     118              : {
     119      1009294 :     if (getInputFlag_) {
     120          103 :         getPowerManagerInput(state);
     121          103 :         getInputFlag_ = false;
     122              :     }
     123              : 
     124      1009294 :     if (state.dataGlobal->MetersHaveBeenInitialized && setupMeterIndexFlag_) {
     125           64 :         setupMeterIndices(state);
     126           64 :         setupMeterIndexFlag_ = false;
     127              :     }
     128              : 
     129      1009294 :     if (state.dataGlobal->BeginEnvrnFlag && newEnvironmentFlag_) {
     130          477 :         reinitAtBeginEnvironment();
     131          477 :         newEnvironmentFlag_ = false;
     132              :     }
     133      1009294 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     134      1005602 :         newEnvironmentFlag_ = true;
     135              :     }
     136              : 
     137              :     // retrieve data from meters for demand and production
     138      1009294 :     totalBldgElecDemand_ =
     139      1009294 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec;
     140      1009294 :     totalHVACElecDemand_ =
     141      1009294 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     142      1009294 :     totalElectricDemand_ = totalBldgElecDemand_ + totalHVACElecDemand_;
     143      1009294 :     elecProducedPVRate_ =
     144      1009294 :         GetInstantMeterValue(state, elecProducedPVMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     145      1009294 :     elecProducedWTRate_ =
     146      1009294 :         GetInstantMeterValue(state, elecProducedWTMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     147      1009294 :     elecProducedStorageRate_ =
     148      1009294 :         GetInstantMeterValue(state, elecProducedStorageMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     149      1009294 :     elecProducedCoGenRate_ =
     150      1009294 :         GetInstantMeterValue(state, elecProducedCoGenMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     151      1009294 :     elecProducedPowerConversionRate_ = GetInstantMeterValue(state, elecProducedPowerConversionMeterIndex_, OutputProcessor::TimeStepType::System) /
     152      1009294 :                                        (state.dataHVACGlobal->TimeStepSysSec);
     153              : 
     154      1009294 :     wholeBldgRemainingLoad_ = totalElectricDemand_;
     155              : 
     156      1009294 :     if (UpdateMetersOnly) { // just update record keeping, don't resimulate load centers
     157       287262 :         if (facilityPowerInTransformerPresent_) {
     158            0 :             facilityPowerInTransformerObj_->manageTransformers(state, 0.0);
     159              :         }
     160              : 
     161       287262 :         updateWholeBuildingRecords(state);
     162       287262 :         return;
     163              :     }
     164              : 
     165      1266727 :     for (auto &e : elecLoadCenterObjs) {
     166       544695 :         e->manageElecLoadCenter(state, firstHVACIteration, wholeBldgRemainingLoad_);
     167       722032 :     }
     168              : 
     169       722032 :     updateWholeBuildingRecords(state);
     170              :     // The transformer call should be put outside of the "Load Center" loop because
     171              :     // 1) A transformer may be for utility, not for load center
     172              :     // 2) A tansformer may be shared by multiple load centers
     173       722032 :     if (facilityPowerInTransformerPresent_) {
     174            0 :         facilityPowerInTransformerObj_->manageTransformers(state, 0.0);
     175              :     }
     176              : 
     177       722032 :     updateWholeBuildingRecords(state);
     178       722032 :     if (powerOutTransformerObj_ != nullptr) {
     179            0 :         powerOutTransformerObj_->manageTransformers(state, electSurplusRate_);
     180              :     }
     181              : 
     182              :     // Need to simulate through the Elec Manager at least twice to ensure that Heat Recovery information is included.
     183              :     // recheck this, may not be needed now that load centers are called more often.
     184              :     //  Does the IF condition also need to check if any thermal following strategies have been specified?
     185              :     //  That is, if only electrical following schemes, don't need to resimulate?
     186       722032 :     if (firstHVACIteration) {
     187       505708 :         SimElecCircuits = true;
     188              :     } else {
     189       216324 :         SimElecCircuits = false;
     190              :     }
     191              : }
     192              : 
     193          483 : void ElectricPowerServiceManager::reinitZoneGainsAtBeginEnvironment()
     194              : {
     195          483 :     if (facilityPowerInTransformerPresent_) {
     196            0 :         facilityPowerInTransformerObj_->reinitZoneGainsAtBeginEnvironment();
     197              :     }
     198          483 :     if (powerOutTransformerObj_ != nullptr) {
     199            0 :         powerOutTransformerObj_->reinitZoneGainsAtBeginEnvironment();
     200              :     }
     201          483 :     if (numLoadCenters_ > 0) {
     202          604 :         for (auto &e : elecLoadCenterObjs) {
     203          302 :             e->reinitZoneGainsAtBeginEnvironment();
     204          302 :         }
     205              :     }
     206          483 : }
     207              : 
     208          103 : void ElectricPowerServiceManager::getPowerManagerInput(EnergyPlusData &state)
     209              : {
     210              :     static constexpr std::string_view routineName = "ElectricPowerServiceManager::getPowerManagerInput ";
     211              : 
     212          103 :     auto &s_ipsc = state.dataIPShortCut;
     213              : 
     214          103 :     numLoadCenters_ = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Distribution");
     215              : 
     216          103 :     if (numLoadCenters_ > 0) {
     217            2 :         for (int iLoadCenterNum = 1; iLoadCenterNum <= numLoadCenters_; ++iLoadCenterNum) {
     218              :             // call Electric Power Load Center constructor, in place
     219            1 :             elecLoadCenterObjs.emplace_back(new ElectPowerLoadCenter(state, iLoadCenterNum));
     220              :         }
     221              :     } else {
     222              :         // issue #4639. see if there are any generators, inverters, converters, or storage devcies, that really need a ElectricLoadCenter:Distribution
     223          102 :         bool errorsFound(false);
     224          102 :         int numGenLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Generators");
     225          102 :         if (numGenLists > 0) {
     226            0 :             ShowSevereError(state, "ElectricLoadCenter:Generators input object requires an ElectricLoadCenterDistribution input object.");
     227            0 :             errorsFound = true;
     228              :         }
     229          102 :         int numInverters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:Simple");
     230          102 :         numInverters += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:FunctionOfPower");
     231          102 :         numInverters += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:LookUpTable");
     232          102 :         if (numInverters > 0) {
     233            0 :             ShowSevereError(state, "ElectricLoadCenter:Inverter:* input objects require an ElectricLoadCenter:Distribution input object.");
     234            0 :             errorsFound = true;
     235              :         }
     236          102 :         int numStorage = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Storage:Simple");
     237          102 :         numStorage += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Storage:Battery");
     238          102 :         if (numStorage > 0) {
     239            0 :             ShowSevereError(state, "ElectricLoadCenter:Storage:* input objects require an ElectricLoadCenter:Distribution input object.");
     240            0 :             errorsFound = true;
     241              :         }
     242          102 :         int numGenerators = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:InternalCombustionEngine");
     243          102 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:CombustionTurbine");
     244          102 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:MicroCHP");
     245          102 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:FuelCell");
     246          102 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:Photovoltaic");
     247          102 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:WindTurbine");
     248          102 :         if (numGenerators > 0) {
     249            0 :             ShowSevereError(state, "Electric generator input objects require an ElectricLoadCenter:Distribution input object.");
     250            0 :             errorsFound = true;
     251              :         }
     252              : 
     253          102 :         if (errorsFound) {
     254            0 :             ShowFatalError(state, "Simulation halted because of missing input objects related to ElectricLoadCenter.");
     255              :         }
     256              : 
     257              :         // if user input did not include an Electric Load center, create a simple default one here for reporting purposes
     258              :         //   but only if there are any other electricity components set up (yet) for metering
     259          102 :         int anyElectricityPresent = GetMeterIndex(state, "ELECTRICITY:FACILITY");
     260          102 :         int anyPlantLoadProfilePresent = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "LoadProfile:Plant");
     261          102 :         if (anyElectricityPresent > -1 || anyPlantLoadProfilePresent > 0) {
     262           74 :             elecLoadCenterObjs.emplace_back(new ElectPowerLoadCenter(state, 0));
     263           74 :             numLoadCenters_ = 1;
     264              :         }
     265              :     }
     266              : 
     267              :     // see if there are any transformers of the type PowerInFromGrid
     268          103 :     numTransformers_ = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Transformer");
     269              : 
     270          103 :     if (numTransformers_ > 0) {
     271              :         int numAlphas; // Number of elements in the alpha array
     272              :         int numNums;   // Number of elements in the numeric array
     273              :         int iOStat;    // IO Status when calling get input subroutine
     274            0 :         bool foundInFromGridTransformer = false;
     275              : 
     276            0 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
     277            0 :         for (int loopTransformer = 1; loopTransformer <= numTransformers_; ++loopTransformer) {
     278            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     279            0 :                                                                      s_ipsc->cCurrentModuleObject,
     280              :                                                                      loopTransformer,
     281            0 :                                                                      s_ipsc->cAlphaArgs,
     282              :                                                                      numAlphas,
     283            0 :                                                                      s_ipsc->rNumericArgs,
     284              :                                                                      numNums,
     285              :                                                                      iOStat,
     286            0 :                                                                      s_ipsc->lNumericFieldBlanks,
     287            0 :                                                                      s_ipsc->lAlphaFieldBlanks,
     288            0 :                                                                      s_ipsc->cAlphaFieldNames,
     289            0 :                                                                      s_ipsc->cNumericFieldNames);
     290              : 
     291            0 :             if (Util::SameString(s_ipsc->cAlphaArgs(3), "PowerInFromGrid")) {
     292            0 :                 if (!foundInFromGridTransformer) {
     293            0 :                     foundInFromGridTransformer = true;
     294            0 :                     facilityPowerInTransformerName_ = s_ipsc->cAlphaArgs(1);
     295            0 :                     facilityPowerInTransformerPresent_ = true;
     296              :                 } else {
     297              :                     // should only have one transformer in input that is PowerInFromGrid
     298            0 :                     ShowWarningError(state, format("{}{}=\"{}\", invalid entry.", routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
     299            0 :                     ShowContinueError(state, format("Invalid {} = {}", s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3)));
     300            0 :                     ShowContinueError(state,
     301              :                                       "Only one transformer with Usage PowerInFromGrid can be used, first one in input file will be used and the "
     302              :                                       "simulation continues...");
     303              :                 }
     304            0 :             } else if (Util::SameString(s_ipsc->cAlphaArgs(3), "PowerOutToGrid")) {
     305            0 :                 if (powerOutTransformerObj_ == nullptr) {
     306            0 :                     ++numPowerOutTransformers_;
     307            0 :                     powerOutTransformerName_ = s_ipsc->cAlphaArgs(1);
     308            0 :                     powerOutTransformerObj_ = std::make_unique<ElectricTransformer>(state, powerOutTransformerName_);
     309              : 
     310              :                 } else {
     311            0 :                     ShowWarningError(state,
     312              :                                      "Found more than one transformer set to PowerOutFromOnsiteGeneration, however only the first one will be used.");
     313              :                 }
     314              :             }
     315              :         }
     316            0 :         if (foundInFromGridTransformer) {
     317              :             // call transformer constructor
     318            0 :             facilityPowerInTransformerObj_ = std::make_unique<ElectricTransformer>(state, facilityPowerInTransformerName_);
     319              :         }
     320              :     } // if transformers
     321              : 
     322          103 :     if (numLoadCenters_ > 0) {
     323          150 :         SetupOutputVariable(state,
     324              :                             "Facility Total Purchased Electricity Rate",
     325              :                             Constant::Units::W,
     326           75 :                             electPurchRate_,
     327              :                             OutputProcessor::TimeStepType::System,
     328              :                             OutputProcessor::StoreType::Average,
     329           75 :                             name_);
     330          150 :         SetupOutputVariable(state,
     331              :                             "Facility Total Purchased Electricity Energy",
     332              :                             Constant::Units::J,
     333           75 :                             electricityPurch_,
     334              :                             OutputProcessor::TimeStepType::System,
     335              :                             OutputProcessor::StoreType::Sum,
     336           75 :                             name_,
     337              :                             Constant::eResource::ElectricityPurchased,
     338              :                             OutputProcessor::Group::Plant,
     339              :                             OutputProcessor::EndUseCat::Cogeneration);
     340              : 
     341          150 :         SetupOutputVariable(state,
     342              :                             "Facility Total Surplus Electricity Rate",
     343              :                             Constant::Units::W,
     344           75 :                             electSurplusRate_,
     345              :                             OutputProcessor::TimeStepType::System,
     346              :                             OutputProcessor::StoreType::Average,
     347           75 :                             name_);
     348          150 :         SetupOutputVariable(state,
     349              :                             "Facility Total Surplus Electricity Energy",
     350              :                             Constant::Units::J,
     351           75 :                             electricitySurplus_,
     352              :                             OutputProcessor::TimeStepType::System,
     353              :                             OutputProcessor::StoreType::Sum,
     354           75 :                             name_,
     355              :                             Constant::eResource::ElectricitySurplusSold,
     356              :                             OutputProcessor::Group::Plant,
     357              :                             OutputProcessor::EndUseCat::Cogeneration);
     358              : 
     359          150 :         SetupOutputVariable(state,
     360              :                             "Facility Net Purchased Electricity Rate",
     361              :                             Constant::Units::W,
     362           75 :                             electricityNetRate_,
     363              :                             OutputProcessor::TimeStepType::System,
     364              :                             OutputProcessor::StoreType::Average,
     365           75 :                             name_);
     366          150 :         SetupOutputVariable(state,
     367              :                             "Facility Net Purchased Electricity Energy",
     368              :                             Constant::Units::J,
     369           75 :                             electricityNet_,
     370              :                             OutputProcessor::TimeStepType::System,
     371              :                             OutputProcessor::StoreType::Sum,
     372           75 :                             name_,
     373              :                             Constant::eResource::ElectricityNet,
     374              :                             OutputProcessor::Group::Plant,
     375              :                             OutputProcessor::EndUseCat::Cogeneration);
     376              : 
     377          150 :         SetupOutputVariable(state,
     378              :                             "Facility Total Building Electricity Demand Rate",
     379              :                             Constant::Units::W,
     380           75 :                             totalBldgElecDemand_,
     381              :                             OutputProcessor::TimeStepType::System,
     382              :                             OutputProcessor::StoreType::Average,
     383           75 :                             name_);
     384          150 :         SetupOutputVariable(state,
     385              :                             "Facility Total HVAC Electricity Demand Rate",
     386              :                             Constant::Units::W,
     387           75 :                             totalHVACElecDemand_,
     388              :                             OutputProcessor::TimeStepType::System,
     389              :                             OutputProcessor::StoreType::Average,
     390           75 :                             name_);
     391          150 :         SetupOutputVariable(state,
     392              :                             "Facility Total Electricity Demand Rate",
     393              :                             Constant::Units::W,
     394           75 :                             totalElectricDemand_,
     395              :                             OutputProcessor::TimeStepType::System,
     396              :                             OutputProcessor::StoreType::Average,
     397           75 :                             name_);
     398              : 
     399          150 :         SetupOutputVariable(state,
     400              :                             "Facility Total Produced Electricity Rate",
     401              :                             Constant::Units::W,
     402           75 :                             electProdRate_,
     403              :                             OutputProcessor::TimeStepType::System,
     404              :                             OutputProcessor::StoreType::Average,
     405           75 :                             name_);
     406          150 :         SetupOutputVariable(state,
     407              :                             "Facility Total Produced Electricity Energy",
     408              :                             Constant::Units::J,
     409           75 :                             electricityProd_,
     410              :                             OutputProcessor::TimeStepType::System,
     411              :                             OutputProcessor::StoreType::Sum,
     412           75 :                             name_);
     413              : 
     414           75 :         reportPVandWindCapacity(state);
     415              : 
     416           75 :         sumUpNumberOfStorageDevices();
     417              : 
     418           75 :         checkLoadCenters(state);
     419              :     }
     420          103 : }
     421              : 
     422           64 : void ElectricPowerServiceManager::setupMeterIndices(EnergyPlusData &state)
     423              : {
     424          128 :     elecFacilityMeterIndex_ = GetMeterIndex(state, "ELECTRICITY:FACILITY");
     425          128 :     elecProducedCoGenMeterIndex_ = GetMeterIndex(state, "COGENERATION:ELECTRICITYPRODUCED");
     426          128 :     elecProducedPVMeterIndex_ = GetMeterIndex(state, "PHOTOVOLTAIC:ELECTRICITYPRODUCED");
     427          128 :     elecProducedWTMeterIndex_ = GetMeterIndex(state, "WINDTURBINE:ELECTRICITYPRODUCED");
     428          128 :     elecProducedStorageMeterIndex_ = GetMeterIndex(state, "ELECTRICSTORAGE:ELECTRICITYPRODUCED");
     429           64 :     elecProducedPowerConversionMeterIndex_ = GetMeterIndex(state, "POWERCONVERSION:ELECTRICITYPRODUCED");
     430              : 
     431           64 :     if (numLoadCenters_ > 0) {
     432           92 :         for (auto &e : elecLoadCenterObjs) {
     433           46 :             e->setupLoadCenterMeterIndices(state);
     434           46 :         }
     435              :     }
     436           64 :     if (facilityPowerInTransformerPresent_) {
     437            0 :         facilityPowerInTransformerObj_->setupMeterIndices(state);
     438              :     }
     439           64 : }
     440              : 
     441          477 : void ElectricPowerServiceManager::reinitAtBeginEnvironment()
     442              : {
     443          477 :     wholeBldgRemainingLoad_ = 0.0;
     444          477 :     electricityProd_ = 0.0;
     445          477 :     electProdRate_ = 0.0;
     446          477 :     electricityPurch_ = 0.0;
     447          477 :     electPurchRate_ = 0.0;
     448          477 :     electSurplusRate_ = 0.0;
     449          477 :     electricitySurplus_ = 0.0;
     450          477 :     electricityNetRate_ = 0.0;
     451          477 :     electricityNet_ = 0.0;
     452          477 :     totalBldgElecDemand_ = 0.0;
     453          477 :     totalHVACElecDemand_ = 0.0;
     454          477 :     totalElectricDemand_ = 0.0;
     455          477 :     elecProducedPVRate_ = 0.0;
     456          477 :     elecProducedWTRate_ = 0.0;
     457          477 :     elecProducedStorageRate_ = 0.0;
     458          477 :     elecProducedCoGenRate_ = 0.0;
     459              : 
     460          477 :     if (numLoadCenters_ > 0) {
     461          750 :         for (auto &e : elecLoadCenterObjs) {
     462          375 :             e->reinitAtBeginEnvironment();
     463          375 :         }
     464              :     }
     465          477 :     if (facilityPowerInTransformerPresent_) {
     466            0 :         facilityPowerInTransformerObj_->reinitAtBeginEnvironment();
     467              :     }
     468          477 :     if (powerOutTransformerObj_ != nullptr) {
     469            0 :         powerOutTransformerObj_->reinitAtBeginEnvironment();
     470              :     }
     471          477 : }
     472              : 
     473           73 : void ElectricPowerServiceManager::verifyCustomMetersElecPowerMgr(EnergyPlusData &state)
     474              : {
     475          127 :     for (std::size_t loop = 0; loop < elecLoadCenterObjs.size(); ++loop) {
     476           54 :         elecLoadCenterObjs[loop]->setupLoadCenterMeterIndices(state);
     477              :     }
     478           73 : }
     479              : 
     480      1731326 : void ElectricPowerServiceManager::updateWholeBuildingRecords(EnergyPlusData &state)
     481              : {
     482              : 
     483              :     // main panel balancing.
     484      1731326 :     totalBldgElecDemand_ =
     485      1731326 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec;
     486      1731326 :     totalHVACElecDemand_ =
     487      1731326 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     488      1731326 :     totalElectricDemand_ = totalBldgElecDemand_ + totalHVACElecDemand_;
     489      1731326 :     elecProducedPVRate_ =
     490      1731326 :         GetInstantMeterValue(state, elecProducedPVMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     491      1731326 :     elecProducedWTRate_ =
     492      1731326 :         GetInstantMeterValue(state, elecProducedWTMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     493      1731326 :     elecProducedStorageRate_ =
     494      1731326 :         GetInstantMeterValue(state, elecProducedStorageMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     495      1731326 :     elecProducedCoGenRate_ =
     496      1731326 :         GetInstantMeterValue(state, elecProducedCoGenMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     497      1731326 :     elecProducedPowerConversionRate_ = GetInstantMeterValue(state, elecProducedPowerConversionMeterIndex_, OutputProcessor::TimeStepType::System) /
     498      1731326 :                                        (state.dataHVACGlobal->TimeStepSysSec);
     499              : 
     500      1731326 :     electProdRate_ = elecProducedCoGenRate_ + elecProducedPVRate_ + elecProducedWTRate_ + elecProducedStorageRate_ + elecProducedPowerConversionRate_;
     501      1731326 :     electricityProd_ = electProdRate_ * state.dataHVACGlobal->TimeStepSysSec; // whole building
     502              : 
     503              :     // Report the Total Electric Power Purchased [W], If negative then there is extra power to be sold or stored.
     504      1731326 :     electPurchRate_ = totalElectricDemand_ - electProdRate_;
     505              :     // Check this value against a tolerance to aid in reporting.
     506      1731326 :     if (std::abs(electPurchRate_) < 0.0001) {
     507       799709 :         electPurchRate_ = 0.0;
     508              :     }
     509      1731326 :     if (electPurchRate_ < 0.0) {
     510            0 :         electPurchRate_ = 0.0; // don't want negative purchased...
     511              :     }
     512              : 
     513              :     // Report the Total Electric Energy Purchased [J]
     514      1731326 :     electricityPurch_ = electPurchRate_ * state.dataHVACGlobal->TimeStepSysSec;
     515              : 
     516              :     // report the total electric surplus....
     517      1731326 :     electSurplusRate_ = electProdRate_ - totalElectricDemand_;
     518      1731326 :     if (std::abs(electSurplusRate_) < 0.0001) {
     519       799709 :         electSurplusRate_ = 0.0;
     520              :     }
     521      1731326 :     if (electSurplusRate_ < 0.0) {
     522       931617 :         electSurplusRate_ = 0.0; // don't want negative surplus
     523              :     }
     524              : 
     525      1731326 :     electricitySurplus_ = electSurplusRate_ * state.dataHVACGlobal->TimeStepSysSec;
     526              : 
     527              :     // report the net electricity , + is purchased, - is surplus
     528      1731326 :     electricityNetRate_ = totalElectricDemand_ - electProdRate_;
     529              : 
     530      1731326 :     electricityNet_ = electricityNetRate_ * state.dataHVACGlobal->TimeStepSysSec;
     531      1731326 : }
     532              : 
     533           75 : void ElectricPowerServiceManager::reportPVandWindCapacity(EnergyPlusData &state)
     534              : {
     535              :     // LEED report
     536           75 :     pvTotalCapacity_ = 0.0;
     537           75 :     windTotalCapacity_ = 0.0;
     538          150 :     for (auto const &lc : elecLoadCenterObjs) {
     539           75 :         if (lc->numGenerators > 0) {
     540            2 :             for (auto const &g : lc->elecGenCntrlObj) {
     541            1 :                 if (g->generatorType == GeneratorType::PV) {
     542            0 :                     pvTotalCapacity_ += g->maxPowerOut;
     543              :                 }
     544            1 :                 if (g->generatorType == GeneratorType::WindTurbine) {
     545            0 :                     windTotalCapacity_ += g->maxPowerOut;
     546              :                 }
     547            1 :             }
     548              :         }
     549           75 :     }
     550              :     // put in total capacity for PV and Wind for LEED report
     551           75 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedRenRatCap, "Photovoltaic", pvTotalCapacity_ / 1000, 2);
     552           75 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedRenRatCap, "Wind", windTotalCapacity_ / 1000, 2);
     553              : 
     554              :     // future work: this legacy approach is relying on the correct power output to have been placed in the Generator list.  There could be a
     555              :     // difference between this control input and the actual size of the systems as defined in the generator objects themselves.  This method should be
     556              :     // replaced with queries that check the capacity from the generator models.
     557           75 : }
     558              : 
     559           75 : void ElectricPowerServiceManager::sumUpNumberOfStorageDevices()
     560              : {
     561           75 :     numElecStorageDevices = 0;
     562          150 :     for (auto const &e : elecLoadCenterObjs) {
     563           75 :         if (e->storageObj != nullptr) {
     564            0 :             ++numElecStorageDevices;
     565              :         }
     566           75 :     }
     567           75 : }
     568              : 
     569           75 : void ElectricPowerServiceManager::checkLoadCenters(EnergyPlusData &state)
     570              : {
     571              : 
     572              :     // issue #5302, detect if storage used on more than one load center. This is really a kind of GlobalNames issue.
     573              :     // expanded to all devices on a load center
     574           75 :     bool errorsFound = false;
     575              : 
     576              :     // first fill in a vector of names
     577           75 :     std::vector<std::string> storageNames;
     578           75 :     std::vector<std::string> genListNames;
     579           75 :     std::vector<std::string> inverterNames;
     580           75 :     std::vector<std::string> converterNames;
     581           75 :     std::vector<std::string> transformerNames;
     582          150 :     for (auto &e : elecLoadCenterObjs) {
     583           75 :         if (e->storageObj != nullptr) {
     584            0 :             storageNames.emplace_back(e->storageObj->name());
     585              :         }
     586           75 :         if (!e->elecGenCntrlObj.empty()) {
     587            1 :             genListNames.emplace_back(e->generatorListName());
     588              :         }
     589           75 :         if (e->inverterObj != nullptr) {
     590            0 :             inverterNames.emplace_back(e->inverterObj->name());
     591              :         }
     592           75 :         if (e->converterObj != nullptr) {
     593            0 :             converterNames.emplace_back(e->converterObj->name());
     594              :         }
     595           75 :         if (e->transformerObj != nullptr) {
     596            0 :             transformerNames.emplace_back(e->transformerObj->name());
     597              :         }
     598           75 :     }
     599              : 
     600              :     // then check the vectors for duplicates.
     601           75 :     for (std::size_t i = 0; i < storageNames.size(); ++i) {
     602            0 :         for (std::size_t j = 0; j < storageNames.size(); ++j) {
     603            0 :             if (storageNames[i] == storageNames[j] && i != j) {
     604            0 :                 ShowSevereError(state,
     605            0 :                                 format("ElectricPowerServiceManager::checkLoadCenters, the electrical storage device named = {} is used in more than "
     606              :                                        "one ElectricLoadCenter:Distribution input object.",
     607            0 :                                        storageNames[i]));
     608            0 :                 ShowContinueError(state, "Electric Load Centers cannot share the same storage device.");
     609            0 :                 errorsFound = true;
     610            0 :                 break;
     611              :             }
     612              :         }
     613            0 :         if (errorsFound) {
     614            0 :             break;
     615              :         }
     616              :     }
     617              : 
     618           76 :     for (std::size_t i = 0; i < genListNames.size(); ++i) {
     619            2 :         for (std::size_t j = 0; j < genListNames.size(); ++j) {
     620            1 :             if (genListNames[i] == genListNames[j] && i != j) {
     621            0 :                 ShowSevereError(state,
     622            0 :                                 format("ElectricPowerServiceManager::checkLoadCenters, the generator list named = {} is used in more than one "
     623              :                                        "ElectricLoadCenter:Distribution input object.",
     624            0 :                                        genListNames[i]));
     625            0 :                 ShowContinueError(state, "Electric Load Centers cannot share the same generator list (ElectricLoadCenter:Generators).");
     626            0 :                 errorsFound = true;
     627            0 :                 break;
     628              :             }
     629              :         }
     630            1 :         if (errorsFound) {
     631            0 :             break;
     632              :         }
     633              :     }
     634              : 
     635           75 :     for (std::size_t i = 0; i < inverterNames.size(); ++i) {
     636            0 :         for (std::size_t j = 0; j < inverterNames.size(); ++j) {
     637            0 :             if (inverterNames[i] == inverterNames[j] && i != j) {
     638            0 :                 ShowSevereError(state,
     639            0 :                                 format("ElectricPowerServiceManager::checkLoadCenters, the inverter device named = {} is used in more than one "
     640              :                                        "ElectricLoadCenter:Distribution input object.",
     641            0 :                                        inverterNames[i]));
     642            0 :                 ShowContinueError(state, "Electric Load Centers cannot share the same inverter device.");
     643            0 :                 errorsFound = true;
     644            0 :                 break;
     645              :             }
     646              :         }
     647            0 :         if (errorsFound) {
     648            0 :             break;
     649              :         }
     650              :     }
     651              : 
     652           75 :     for (std::size_t i = 0; i < converterNames.size(); ++i) {
     653            0 :         for (std::size_t j = 0; j < converterNames.size(); ++j) {
     654            0 :             if (converterNames[i] == converterNames[j] && i != j) {
     655            0 :                 ShowSevereError(state,
     656            0 :                                 format("ElectricPowerServiceManager::checkLoadCenters, the converter device named = {} is used in more than one "
     657              :                                        "ElectricLoadCenter:Distribution input object.",
     658            0 :                                        converterNames[i]));
     659            0 :                 ShowContinueError(state, "Electric Load Centers cannot share the same converter device.");
     660            0 :                 errorsFound = true;
     661            0 :                 break;
     662              :             }
     663              :         }
     664            0 :         if (errorsFound) {
     665            0 :             break;
     666              :         }
     667              :     }
     668              : 
     669           75 :     for (std::size_t i = 0; i < transformerNames.size(); ++i) {
     670            0 :         for (std::size_t j = 0; j < transformerNames.size(); ++j) {
     671            0 :             if (transformerNames[i] == transformerNames[j] && i != j) {
     672            0 :                 ShowSevereError(state,
     673            0 :                                 format("ElectricPowerServiceManager::checkLoadCenters, the transformer device named = {} is used in more than one "
     674              :                                        "ElectricLoadCenter:Distribution input object.",
     675            0 :                                        transformerNames[i]));
     676            0 :                 ShowContinueError(state, "Electric Load Centers cannot share the same transformer device.");
     677            0 :                 errorsFound = true;
     678            0 :                 break;
     679              :             }
     680              :         }
     681            0 :         if (errorsFound) {
     682            0 :             break;
     683              :         }
     684              :     }
     685              : 
     686           75 :     if (errorsFound) { // throw fatal, these errors could fatal out in internal gains with missleading data
     687            0 :         ShowFatalError(state, "ElectricPowerServiceManager::checkLoadCenters, preceding errors terminate program.");
     688              :     }
     689           75 : }
     690              : 
     691              : // TODO: Absolutely not. Constructors should not do this much work,
     692              : // because if this fails, then the constructor fails and who knows who
     693              : // will be referencing this object.
     694           85 : ElectPowerLoadCenter::ElectPowerLoadCenter(EnergyPlusData &state, int const objectNum)
     695          170 :     : numGenerators(0), bussType(ElectricBussType::Invalid), thermalProd(0.0), thermalProdRate(0.0), inverterPresent(false),
     696           85 :       subpanelFeedInRequest(0.0), subpanelFeedInRate(0.0), subpanelDrawRate(0.0), genElectricProd(0.0), genElectProdRate(0.0), storOpCVDrawRate(0.0),
     697           85 :       storOpCVFeedInRate(0.0), storOpCVChargeRate(0.0), storOpCVDischargeRate(0.0), storOpIsCharging(false), storOpIsDischarging(false),
     698          255 :       genOperationScheme_(GeneratorOpScheme::Invalid), demandMeterPtr_(0), generatorsPresent_(false), myCoGenSetupFlag_(true), demandLimit_(0.0),
     699          255 :       storagePresent_(false), transformerPresent_(false), totalPowerRequest_(0.0), totalThermalPowerRequest_(0.0),
     700          255 :       storageScheme_(StorageOpScheme::Invalid), trackStorageOpMeterIndex_(0), converterPresent_(false), maxStorageSOCFraction_(1.0),
     701           85 :       minStorageSOCFraction_(0.0), designStorageChargePower_(0.0), designStorageChargePowerWasSet_(false), designStorageDischargePower_(0.0),
     702           85 :       designStorageDischargePowerWasSet_(false), facilityDemandTarget_(0.0), eMSOverridePelFromStorage_(false), // if true, EMS calling for override
     703           85 :       eMSValuePelFromStorage_(0.0),      // value EMS is directing to use, power from storage [W]
     704           85 :       eMSOverridePelIntoStorage_(false), // if true, EMS calling for override
     705           85 :       eMSValuePelIntoStorage_(0.0)       // value EMS is directing to use, power into storage [W]
     706              : {
     707              : 
     708              :     static constexpr std::string_view routineName = "ElectPowerLoadCenter constructor ";
     709              :     int numAlphas; // Number of elements in the alpha array
     710              :     int numNums;   // Number of elements in the numeric array
     711              :     int IOStat;    // IO Status when calling get input subroutine
     712              : 
     713           85 :     auto &s_ipsc = state.dataIPShortCut;
     714              : 
     715           85 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Distribution";
     716           85 :     bool errorsFound = false;
     717           85 :     if (objectNum > 0) {
     718           22 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     719           11 :                                                                  s_ipsc->cCurrentModuleObject,
     720              :                                                                  objectNum,
     721           11 :                                                                  s_ipsc->cAlphaArgs,
     722              :                                                                  numAlphas,
     723           11 :                                                                  s_ipsc->rNumericArgs,
     724              :                                                                  numNums,
     725              :                                                                  IOStat,
     726           11 :                                                                  s_ipsc->lNumericFieldBlanks,
     727           11 :                                                                  s_ipsc->lAlphaFieldBlanks,
     728           11 :                                                                  s_ipsc->cAlphaFieldNames,
     729           11 :                                                                  s_ipsc->cNumericFieldNames);
     730              : 
     731           11 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     732              : 
     733           11 :         name_ = s_ipsc->cAlphaArgs(1);
     734              :         // how to verify names are unique across objects? add to GlobalNames?
     735              : 
     736           11 :         if (!s_ipsc->lAlphaFieldBlanks(2)) {
     737           11 :             generatorListName_ = s_ipsc->cAlphaArgs(2);
     738              :             // check that
     739              : 
     740           11 :             int testIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Generators", generatorListName_);
     741           11 :             if (testIndex == 0) {
     742            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     743            0 :                 errorsFound = true;
     744              :             }
     745              :         }
     746              : 
     747           11 :         if (!s_ipsc->lAlphaFieldBlanks(3)) {
     748              :             // Load the Generator Control Operation Scheme
     749           11 :             genOperationScheme_ = static_cast<GeneratorOpScheme>(getEnumValue(generatorOpSchemeNamesUC, s_ipsc->cAlphaArgs(3)));
     750           11 :             if (genOperationScheme_ == GeneratorOpScheme::Invalid) {
     751            0 :                 ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     752            0 :                 errorsFound = true;
     753              :             }
     754              :         }
     755              : 
     756           11 :         demandLimit_ = s_ipsc->rNumericArgs(1);
     757              : 
     758           11 :         if (genOperationScheme_ == GeneratorOpScheme::TrackSchedule) {
     759            0 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
     760            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
     761            0 :                 errorsFound = true;
     762            0 :             } else if ((trackSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
     763            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
     764            0 :                 errorsFound = true;
     765              :             }
     766              :         }
     767              : 
     768           11 :         demandMeterName_ = Util::makeUPPER(s_ipsc->cAlphaArgs(5));
     769              :         // meters may not be "loaded" yet, defered check to later subroutine
     770              : 
     771           11 :         if (s_ipsc->cAlphaArgs(6).empty()) {
     772            0 :             bussType = ElectricBussType::ACBuss;
     773           11 :         } else if ((bussType = static_cast<ElectricBussType>(getEnumValue(electricBussTypeNamesUC, s_ipsc->cAlphaArgs(6)))) ==
     774              :                    ElectricBussType::Invalid) {
     775            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     776            0 :             errorsFound = true;
     777           11 :         } else if (bussType == ElectricBussType::DCBussInverter) {
     778            4 :             inverterPresent = true;
     779            7 :         } else if (bussType == ElectricBussType::ACBussStorage) {
     780            1 :             storagePresent_ = true;
     781            6 :         } else if (bussType == ElectricBussType::DCBussInverterDCStorage || bussType == ElectricBussType::DCBussInverterACStorage) {
     782            3 :             inverterPresent = true;
     783            3 :             storagePresent_ = true;
     784              :         }
     785              : 
     786           11 :         if (inverterPresent) {
     787            7 :             if (!s_ipsc->lAlphaFieldBlanks(7)) {
     788            7 :                 inverterName = s_ipsc->cAlphaArgs(7);
     789              :             } else {
     790            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     791            0 :                 errorsFound = true;
     792              :             }
     793              :         }
     794              : 
     795           11 :         if (storagePresent_) {
     796            4 :             if (!s_ipsc->lAlphaFieldBlanks(8)) {
     797            4 :                 storageName_ = s_ipsc->cAlphaArgs(8);
     798              :             } else {
     799            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     800            0 :                 errorsFound = true;
     801              :             }
     802              :         }
     803              : 
     804           11 :         if (!s_ipsc->lAlphaFieldBlanks(9)) {
     805              :             // process transformer
     806            1 :             transformerName_ = s_ipsc->cAlphaArgs(9);
     807              :             // only transformers of use type powerFromLoadCenterToBldg are really held in a load center, The legacy applications for transformers are
     808              :             // held at the higher Electric service level
     809            1 :             transformerPresent_ = true;
     810              :         }
     811              : 
     812              :         // Begin new content for grid supply and more control over storage
     813              :         // user selected storage operation scheme
     814           11 :         if (s_ipsc->lAlphaFieldBlanks(10)) {
     815           10 :             storageScheme_ = StorageOpScheme::FacilityDemandStoreExcessOnSite;
     816            1 :         } else if ((storageScheme_ = static_cast<StorageOpScheme>(getEnumValue(storageOpSchemeNamesUC, s_ipsc->cAlphaArgs(10)))) ==
     817              :                    StorageOpScheme::Invalid) {
     818            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     819            0 :             errorsFound = true;
     820              :         }
     821              : 
     822           11 :         if (!s_ipsc->lAlphaFieldBlanks(11)) {
     823            0 :             trackSorageOpMeterName_ = s_ipsc->cAlphaArgs(11);
     824           11 :         } else if (storageScheme_ == StorageOpScheme::MeterDemandStoreExcessOnSite) { // throw error
     825            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(11), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     826            0 :             errorsFound = true;
     827              :         }
     828              : 
     829           11 :         if (!s_ipsc->lAlphaFieldBlanks(12)) {
     830            0 :             converterName_ = s_ipsc->cAlphaArgs(12);
     831            0 :             converterPresent_ = true;
     832           11 :         } else if (storageScheme_ == StorageOpScheme::ChargeDischargeSchedules || storageScheme_ == StorageOpScheme::FacilityDemandLeveling) {
     833            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(12), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     834            0 :             errorsFound = true;
     835              :         }
     836              : 
     837           11 :         if (s_ipsc->lNumericFieldBlanks(2)) {
     838           11 :             maxStorageSOCFraction_ = 1.0;
     839              :         } else {
     840            0 :             maxStorageSOCFraction_ = s_ipsc->rNumericArgs(2);
     841              :         }
     842           11 :         if (s_ipsc->lNumericFieldBlanks(3)) {
     843           11 :             minStorageSOCFraction_ = 0.0;
     844              :         } else {
     845            0 :             minStorageSOCFraction_ = s_ipsc->rNumericArgs(3);
     846              :         }
     847           11 :         if (s_ipsc->lNumericFieldBlanks(4)) {
     848            9 :             designStorageChargePowerWasSet_ = false;
     849              :         } else {
     850            2 :             designStorageChargePowerWasSet_ = true;
     851            2 :             designStorageChargePower_ = s_ipsc->rNumericArgs(4);
     852              :         }
     853           11 :         if (s_ipsc->lNumericFieldBlanks(5)) {
     854            9 :             designStorageDischargePowerWasSet_ = false;
     855              :         } else {
     856            2 :             designStorageDischargePowerWasSet_ = true;
     857            2 :             designStorageDischargePower_ = s_ipsc->rNumericArgs(5);
     858              :         }
     859              : 
     860           11 :         if (!s_ipsc->lNumericFieldBlanks(6)) {
     861            0 :             facilityDemandTarget_ = s_ipsc->rNumericArgs(6);
     862           11 :         } else if (storageScheme_ == StorageOpScheme::FacilityDemandLeveling) {
     863            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cNumericFieldNames(6), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     864            0 :             errorsFound = true;
     865              :         }
     866              : 
     867           11 :         if (storageScheme_ == StorageOpScheme::ChargeDischargeSchedules) {
     868            0 :             if (s_ipsc->lAlphaFieldBlanks(13)) {
     869            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(13), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     870            0 :                 errorsFound = true;
     871            0 :             } else if ((storageChargeModSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(13))) == nullptr) {
     872            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(13), s_ipsc->cAlphaArgs(13));
     873            0 :                 errorsFound = true;
     874              :             }
     875              : 
     876            0 :             if (s_ipsc->lAlphaFieldBlanks(14)) {
     877            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(14), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     878            0 :                 errorsFound = true;
     879            0 :             } else if ((storageDischargeModSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(14))) == nullptr) {
     880            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(14), s_ipsc->cAlphaArgs(14));
     881            0 :                 errorsFound = true;
     882              :             }
     883              :         }
     884              : 
     885           11 :         if (storageScheme_ == StorageOpScheme::FacilityDemandLeveling) {
     886            0 :             if (s_ipsc->lAlphaFieldBlanks(15)) {
     887            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(15), s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     888            0 :                 errorsFound = true;
     889            0 :             } else if ((facilityDemandTargetModSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(15))) == nullptr) {
     890            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(15), s_ipsc->cAlphaArgs(15));
     891            0 :                 errorsFound = true;
     892              :             }
     893              :         }
     894              : 
     895              :     } else { // object num == 0
     896              :         // just construct an empty object and return
     897           74 :         return;
     898              :     }
     899              : 
     900              :     // now that we are done with processing get input for ElectricLoadCenter:Distribution we can call child input objects without IP shortcut problems
     901           11 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Generators";
     902           11 :     int genListObjectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, s_ipsc->cCurrentModuleObject, generatorListName_);
     903           11 :     if (genListObjectNum > 0) {
     904           22 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     905           11 :                                                                  s_ipsc->cCurrentModuleObject,
     906              :                                                                  genListObjectNum,
     907           11 :                                                                  s_ipsc->cAlphaArgs,
     908              :                                                                  numAlphas,
     909           11 :                                                                  s_ipsc->rNumericArgs,
     910              :                                                                  numNums,
     911              :                                                                  IOStat,
     912           11 :                                                                  s_ipsc->lNumericFieldBlanks,
     913           11 :                                                                  s_ipsc->lAlphaFieldBlanks,
     914           11 :                                                                  s_ipsc->cAlphaFieldNames,
     915           11 :                                                                  s_ipsc->cNumericFieldNames);
     916              : 
     917              :         // Calculate the number of generators in list
     918           11 :         numGenerators = numNums / 2; // note IDD needs Min Fields = 6
     919           11 :         if (mod((numAlphas - 1 + numNums), 5) != 0) {
     920            0 :             ++numGenerators;
     921              :         }
     922           11 :         int alphaCount = 2;
     923           32 :         for (int genCount = 1; genCount <= numGenerators; ++genCount) {
     924              :             // call constructor in place
     925           21 :             generatorsPresent_ = true;
     926           21 :             elecGenCntrlObj.emplace_back(new GeneratorController(state,
     927           21 :                                                                  s_ipsc->cAlphaArgs(alphaCount),
     928           21 :                                                                  s_ipsc->cAlphaArgs(alphaCount + 1),
     929           21 :                                                                  s_ipsc->rNumericArgs(2 * genCount - 1),
     930           21 :                                                                  s_ipsc->cAlphaArgs(alphaCount + 2),
     931           63 :                                                                  s_ipsc->rNumericArgs(2 * genCount)));
     932           21 :             ++alphaCount;
     933           21 :             ++alphaCount;
     934           21 :             ++alphaCount;
     935              :         }
     936              : 
     937              :         // issue #5299 check for non-zero values in thermal electric ratio if gen op scheme is ThermalFollow*
     938           11 :         if (genOperationScheme_ == GeneratorOpScheme::ThermalFollow || genOperationScheme_ == GeneratorOpScheme::ThermalFollowLimitElectrical) {
     939              :             // check to make sure the user didn't input zeros for thermalToElectricControlRatio
     940            0 :             for (auto const &g : elecGenCntrlObj) {
     941            0 :                 if (g->nominalThermElectRatio <= 0.0) {
     942            0 :                     ShowWarningError(state,
     943            0 :                                      format("Generator operation needs to be based on following thermal loads and needs values for Rated Thermal to "
     944              :                                             "Electrical Power Ratio in {} named {}",
     945            0 :                                             s_ipsc->cCurrentModuleObject,
     946            0 :                                             s_ipsc->cAlphaArgs(1)));
     947              :                 }
     948            0 :             }
     949              :         }
     950              :     }
     951              : 
     952           11 :     if (!errorsFound && inverterPresent) {
     953              :         // call inverter constructor
     954            7 :         inverterObj = std::make_unique<DCtoACInverter>(state, inverterName);
     955              : 
     956              :         // Make sure only Generator::PVWatts are used with Inverter:PVWatts
     957              :         // Add up the total DC capacity and pass it to the inverter.
     958            7 :         if (inverterObj->modelType() == DCtoACInverter::InverterModelType::PVWatts) {
     959            2 :             Real64 totalDCCapacity = 0.0;
     960            6 :             for (const auto &generatorController : elecGenCntrlObj) {
     961            4 :                 if (generatorController->generatorType != GeneratorType::PVWatts) {
     962            0 :                     errorsFound = true;
     963            0 :                     ShowSevereError(state, format("{}ElectricLoadCenter:Distribution=\"{}\",", routineName, name_));
     964            0 :                     ShowContinueError(state, "ElectricLoadCenter:Inverter:PVWatts can only be used with Generator:PVWatts");
     965            0 :                     ShowContinueError(state,
     966            0 :                                       format("\"{}\" is of type {}",
     967            0 :                                              generatorController->name,
     968            0 :                                              generatorTypeNames[static_cast<int>(generatorController->generatorType)]));
     969              :                 } else {
     970            4 :                     totalDCCapacity += generatorController->pvwattsGenerator->getDCSystemCapacity();
     971              : 
     972              :                     // Pass the inverter properties to the PVWatts generator class
     973            4 :                     generatorController->pvwattsGenerator->setDCtoACRatio(inverterObj->pvWattsDCtoACSizeRatio());
     974            4 :                     generatorController->pvwattsGenerator->setInverterEfficiency(inverterObj->pvWattsInverterEfficiency());
     975              :                 }
     976            2 :             }
     977            2 :             if (!errorsFound) {
     978            2 :                 inverterObj->setPVWattsDCCapacity(state, totalDCCapacity);
     979              :             }
     980              :         }
     981              :     }
     982              : 
     983           11 :     if (!errorsFound && storagePresent_) {
     984              :         // call storage constructor
     985            4 :         storageObj = std::make_unique<ElectricStorage>(state, storageName_);
     986              :     }
     987              : 
     988           11 :     if (!errorsFound && transformerPresent_) {
     989              : 
     990            1 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
     991            1 :         int transformerItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, s_ipsc->cCurrentModuleObject, transformerName_);
     992            1 :         if (transformerItemNum > 0) {
     993              :             int iOStat;
     994            2 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     995            1 :                                                                      s_ipsc->cCurrentModuleObject,
     996              :                                                                      transformerItemNum,
     997            1 :                                                                      s_ipsc->cAlphaArgs,
     998              :                                                                      numAlphas,
     999            1 :                                                                      s_ipsc->rNumericArgs,
    1000              :                                                                      numNums,
    1001              :                                                                      iOStat,
    1002            1 :                                                                      s_ipsc->lNumericFieldBlanks,
    1003            1 :                                                                      s_ipsc->lAlphaFieldBlanks,
    1004            1 :                                                                      s_ipsc->cAlphaFieldNames,
    1005            1 :                                                                      s_ipsc->cNumericFieldNames);
    1006            1 :             if (Util::SameString(s_ipsc->cAlphaArgs(3),
    1007              :                                  "LoadCenterPowerConditioning")) { // this is the right kind of transformer
    1008            0 :                 transformerObj = std::make_unique<ElectricTransformer>(state, transformerName_);
    1009              :             } else {
    1010            2 :                 ShowWarningError(
    1011              :                     state,
    1012            3 :                     format("Transformer named {} associated with the load center named {} should have {} set to LoadCenterPowerConditioning.",
    1013            1 :                            transformerName_,
    1014            1 :                            name_,
    1015            1 :                            s_ipsc->cAlphaFieldNames(3)));
    1016              :             }
    1017              :         } else {
    1018            0 :             ShowSevereError(state, format("Transformer named {}, was not found for the load center named {}", transformerName_, name_));
    1019            0 :             errorsFound = true;
    1020              :         }
    1021              :     }
    1022              : 
    1023           11 :     if (!errorsFound && converterPresent_) {
    1024              :         // call AC to DC converter constructor
    1025            0 :         converterObj = std::make_unique<ACtoDCConverter>(state, converterName_);
    1026              :     }
    1027              : 
    1028              :     // Setup general output variables for reporting in the electric load center
    1029           22 :     SetupOutputVariable(state,
    1030              :                         "Electric Load Center Produced Electricity Rate",
    1031              :                         Constant::Units::W,
    1032           11 :                         genElectProdRate,
    1033              :                         OutputProcessor::TimeStepType::System,
    1034              :                         OutputProcessor::StoreType::Average,
    1035           11 :                         name_);
    1036           22 :     SetupOutputVariable(state,
    1037              :                         "Electric Load Center Produced Electricity Energy",
    1038              :                         Constant::Units::J,
    1039           11 :                         genElectricProd,
    1040              :                         OutputProcessor::TimeStepType::System,
    1041              :                         OutputProcessor::StoreType::Sum,
    1042           11 :                         name_);
    1043           22 :     SetupOutputVariable(state,
    1044              :                         "Electric Load Center Supplied Electricity Rate",
    1045              :                         Constant::Units::W,
    1046           11 :                         subpanelFeedInRate,
    1047              :                         OutputProcessor::TimeStepType::System,
    1048              :                         OutputProcessor::StoreType::Average,
    1049           11 :                         name_);
    1050           22 :     SetupOutputVariable(state,
    1051              :                         "Electric Load Center Drawn Electricity Rate",
    1052              :                         Constant::Units::W,
    1053           11 :                         subpanelDrawRate,
    1054              :                         OutputProcessor::TimeStepType::System,
    1055              :                         OutputProcessor::StoreType::Average,
    1056           11 :                         name_);
    1057           22 :     SetupOutputVariable(state,
    1058              :                         "Electric Load Center Produced Thermal Rate",
    1059              :                         Constant::Units::W,
    1060           11 :                         thermalProdRate,
    1061              :                         OutputProcessor::TimeStepType::System,
    1062              :                         OutputProcessor::StoreType::Average,
    1063           11 :                         name_);
    1064           22 :     SetupOutputVariable(state,
    1065              :                         "Electric Load Center Produced Thermal Energy",
    1066              :                         Constant::Units::J,
    1067           11 :                         thermalProd,
    1068              :                         OutputProcessor::TimeStepType::System,
    1069              :                         OutputProcessor::StoreType::Sum,
    1070           11 :                         name_);
    1071           22 :     SetupOutputVariable(state,
    1072              :                         "Electric Load Center Requested Electricity Rate",
    1073              :                         Constant::Units::W,
    1074           11 :                         totalPowerRequest_,
    1075              :                         OutputProcessor::TimeStepType::System,
    1076              :                         OutputProcessor::StoreType::Average,
    1077           11 :                         name_);
    1078              : 
    1079           11 :     if (state.dataGlobal->AnyEnergyManagementSystemInModel && storagePresent_) {
    1080            0 :         SetupEMSActuator(state, "Electrical Storage", name_, "Power Draw Rate", "[W]", eMSOverridePelFromStorage_, eMSValuePelFromStorage_);
    1081            0 :         SetupEMSActuator(state, "Electrical Storage", name_, "Power Charge Rate", "[W]", eMSOverridePelIntoStorage_, eMSValuePelIntoStorage_);
    1082              :     }
    1083              : 
    1084           11 :     if (errorsFound) {
    1085            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    1086              :     }
    1087            0 : }
    1088              : 
    1089       544695 : void ElectPowerLoadCenter::manageElecLoadCenter(EnergyPlusData &state, bool const firstHVACIteration, Real64 &remainingWholePowerDemand)
    1090              : {
    1091              :     //
    1092       544695 :     subpanelFeedInRequest = remainingWholePowerDemand;
    1093              : 
    1094       544695 :     if (generatorsPresent_) {
    1095         2235 :         dispatchGenerators(state, firstHVACIteration, remainingWholePowerDemand);
    1096              : 
    1097              :     } // if generators present
    1098       544695 :     updateLoadCenterGeneratorRecords(state);
    1099       544695 :     if (bussType == ElectricBussType::DCBussInverter || bussType == ElectricBussType::DCBussInverterACStorage) {
    1100            0 :         inverterObj->simulate(state, genElectProdRate);
    1101              :     }
    1102              : 
    1103       544695 :     if (storagePresent_) {
    1104            0 :         storageObj->timeCheckAndUpdate(state);
    1105            0 :         dispatchStorage(state, subpanelFeedInRequest);
    1106              :     }
    1107              : 
    1108       544695 :     if (bussType == ElectricBussType::DCBussInverterDCStorage) {
    1109            0 :         if (inverterObj != nullptr) {
    1110            0 :             inverterObj->simulate(state, storOpCVFeedInRate);
    1111              :         }
    1112              :     }
    1113              : 
    1114       544695 :     if (converterObj != nullptr) {
    1115            0 :         converterObj->simulate(state, storOpCVDrawRate);
    1116              :     }
    1117              : 
    1118       544695 :     if (transformerObj != nullptr) {
    1119            0 :         if (storOpCVFeedInRate > 0.0) {
    1120            0 :             transformerObj->manageTransformers(state, storOpCVFeedInRate);
    1121            0 :         } else if (storOpCVDrawRate > 0.0) {
    1122            0 :             transformerObj->manageTransformers(state, subpanelDrawRate);
    1123              :         }
    1124              :     }
    1125       544695 :     updateLoadCenterGeneratorRecords(state);
    1126       544695 : }
    1127              : 
    1128         2235 : void ElectPowerLoadCenter::dispatchGenerators(EnergyPlusData &state,
    1129              :                                               bool const firstHVACIteration,
    1130              :                                               Real64 &remainingWholePowerDemand // power request in, remaining unmet request out
    1131              : )
    1132              : {
    1133              : 
    1134              :     // This funciton checks generator operation scheme and assigns requests to power generators
    1135              :     // the generators are called to simulate from here and passed some control data
    1136              :     // the actual production from each generator is recorded and accounting tracks how much of the load is met
    1137              : 
    1138              :     // If a generator is needed in the simulation for a small load and it is less than the minimum part load ratio
    1139              :     // the generator will operate at the minimum part load ratio and the excess will either reduce demand or
    1140              :     // be available for storage or sell back to the power company.
    1141              : 
    1142              :     // Both the Demand Limit and Track Electrical schemes will sequentially load the available generators.  All demand
    1143         2235 :     Real64 loadCenterElectricLoad = 0.0;
    1144         2235 :     Real64 remainingLoad = 0.0;
    1145         2235 :     Real64 customMeterDemand = 0.0;
    1146              : 
    1147         2235 :     switch (genOperationScheme_) {
    1148              : 
    1149         2235 :     case GeneratorOpScheme::BaseLoad: {
    1150              : 
    1151         2235 :         loadCenterElectricLoad = remainingWholePowerDemand;
    1152              : 
    1153         4470 :         for (auto &g : elecGenCntrlObj) {
    1154              : 
    1155         2235 :             if (g->availSched->getCurrentVal() > 0.0) {
    1156              :                 // Set the Operation Flag
    1157         2235 :                 g->onThisTimestep = true;
    1158              :                 // Set the electric generator load request
    1159         2235 :                 g->powerRequestThisTimestep = g->maxPowerOut;
    1160              :             } else {
    1161            0 :                 g->onThisTimestep = false;
    1162            0 :                 g->powerRequestThisTimestep = 0.0;
    1163              :             }
    1164              : 
    1165              :             // now handle EMS override
    1166         2235 :             if (g->eMSRequestOn) {
    1167            0 :                 g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1168            0 :                 if (g->powerRequestThisTimestep > 0.0) {
    1169            0 :                     g->onThisTimestep = true;
    1170              :                 } else {
    1171            0 :                     g->onThisTimestep = false;
    1172              :                 }
    1173              :             }
    1174              : 
    1175              :             // Get generator's actual electrical and thermal power outputs
    1176         2235 :             g->simGeneratorGetPowerOutput(
    1177         2235 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1178              : 
    1179         2235 :             totalPowerRequest_ += g->powerRequestThisTimestep;
    1180         2235 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1181         2235 :         }
    1182         2235 :         break;
    1183              :     }
    1184            0 :     case GeneratorOpScheme::DemandLimit: {
    1185              :         // The Demand Limit scheme tries to have the generators meet all of the demand above the purchased Electric
    1186              :         //  limit set by the user.
    1187            0 :         remainingLoad = remainingWholePowerDemand - demandLimit_;
    1188            0 :         loadCenterElectricLoad = remainingLoad;
    1189              : 
    1190            0 :         for (auto &g : elecGenCntrlObj) {
    1191              : 
    1192            0 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1193              :                 // Set the Operation Flag
    1194            0 :                 g->onThisTimestep = true;
    1195              : 
    1196              :                 // Set the electric generator load
    1197            0 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1198              : 
    1199              :                 // now handle EMS override
    1200            0 :                 if (g->eMSRequestOn) {
    1201            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1202            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1203            0 :                         g->onThisTimestep = true;
    1204              :                     } else {
    1205            0 :                         g->onThisTimestep = false;
    1206              :                     }
    1207              :                 }
    1208              :             } else {
    1209            0 :                 g->onThisTimestep = false;
    1210            0 :                 g->powerRequestThisTimestep = 0.0;
    1211              : 
    1212              :                 // now handle EMS override
    1213            0 :                 if (g->eMSRequestOn) {
    1214            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1215            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1216            0 :                         g->onThisTimestep = true;
    1217              :                     } else {
    1218            0 :                         g->onThisTimestep = false;
    1219              :                     }
    1220              :                 }
    1221              :             }
    1222              : 
    1223              :             // Get generator's actual electrical and thermal power outputs
    1224            0 :             g->simGeneratorGetPowerOutput(
    1225            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1226              : 
    1227            0 :             if (g->eMSRequestOn) {
    1228            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1229              :             } else {
    1230            0 :                 if (g->powerRequestThisTimestep > 0.0) {
    1231            0 :                     totalPowerRequest_ += g->maxPowerOut;
    1232            0 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1233              :                 }
    1234              :             }
    1235            0 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1236            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1237            0 :         }
    1238            0 :         break;
    1239              :     }
    1240            0 :     case GeneratorOpScheme::TrackElectrical: {
    1241              :         // The Track Electrical scheme tries to have the generators meet all of the electrical demand for the building.
    1242            0 :         remainingLoad = remainingWholePowerDemand;
    1243            0 :         loadCenterElectricLoad = remainingLoad;
    1244              : 
    1245            0 :         for (auto &g : elecGenCntrlObj) {
    1246              : 
    1247            0 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1248              :                 // Set the Operation Flag
    1249            0 :                 g->onThisTimestep = true;
    1250              : 
    1251              :                 // Set the electric generator load
    1252            0 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1253              : 
    1254              :                 // now handle EMS override
    1255            0 :                 if (g->eMSRequestOn) {
    1256            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1257            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1258            0 :                         g->onThisTimestep = true;
    1259              :                     } else {
    1260            0 :                         g->onThisTimestep = false;
    1261              :                     }
    1262              :                 }
    1263              :             } else {
    1264            0 :                 g->onThisTimestep = false;
    1265            0 :                 g->powerRequestThisTimestep = 0.0;
    1266              :                 // now handle EMS override
    1267            0 :                 if (g->eMSRequestOn) {
    1268            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1269            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1270            0 :                         g->onThisTimestep = true;
    1271              :                     } else {
    1272            0 :                         g->onThisTimestep = false;
    1273              :                     }
    1274              :                 }
    1275              :             }
    1276              : 
    1277              :             // Get generator's actual electrical and thermal power outputs
    1278            0 :             g->simGeneratorGetPowerOutput(
    1279            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1280              : 
    1281            0 :             if (g->eMSRequestOn) {
    1282            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1283              :             } else {
    1284            0 :                 if (g->powerRequestThisTimestep > 0.0) {
    1285            0 :                     totalPowerRequest_ += g->maxPowerOut;
    1286            0 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1287              :                 }
    1288              :             }
    1289            0 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1290            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1291            0 :         }
    1292            0 :         break;
    1293              :     }
    1294            0 :     case GeneratorOpScheme::TrackSchedule: {
    1295              :         // The Track Schedule scheme tries to have the generators meet the electrical demand determined from a schedule.
    1296              :         //  Code is very similar to 'Track Electrical' except for initial RemainingLoad is replaced by SchedElecDemand
    1297              :         //  and PV production is ignored.
    1298            0 :         remainingLoad = trackSched_->getCurrentVal();
    1299            0 :         loadCenterElectricLoad = remainingLoad;
    1300              : 
    1301            0 :         for (auto &g : elecGenCntrlObj) {
    1302              : 
    1303            0 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1304              :                 // Set the Operation Flag
    1305            0 :                 g->onThisTimestep = true;
    1306              : 
    1307              :                 // Set the electric generator load
    1308            0 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1309              : 
    1310              :                 // now handle EMS override
    1311            0 :                 if (g->eMSRequestOn) {
    1312            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1313            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1314            0 :                         g->onThisTimestep = true;
    1315              :                     } else {
    1316            0 :                         g->onThisTimestep = false;
    1317              :                     }
    1318              :                 }
    1319              :             } else {
    1320            0 :                 g->onThisTimestep = false;
    1321            0 :                 g->powerRequestThisTimestep = 0.0;
    1322              : 
    1323              :                 // now handle EMS override
    1324            0 :                 if (g->eMSRequestOn) {
    1325            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1326            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1327            0 :                         g->onThisTimestep = true;
    1328              :                     } else {
    1329            0 :                         g->onThisTimestep = false;
    1330              :                     }
    1331              :                 }
    1332              :             }
    1333              : 
    1334              :             // Get generator's actual electrical and thermal power outputs
    1335            0 :             g->simGeneratorGetPowerOutput(
    1336            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1337              : 
    1338            0 :             if (g->eMSRequestOn) {
    1339            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1340              :             } else {
    1341            0 :                 if (g->powerRequestThisTimestep > 0.0) {
    1342            0 :                     totalPowerRequest_ += g->maxPowerOut;
    1343            0 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1344              :                 }
    1345              :             }
    1346            0 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1347            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1348            0 :         }
    1349            0 :         break;
    1350              :     }
    1351            0 :     case GeneratorOpScheme::TrackMeter: {
    1352              :         // The TRACK CUSTOM METER scheme tries to have the generators meet all of the
    1353              :         //   electrical demand from a meter, it can also be a user-defined Custom Meter
    1354              :         //   and PV is ignored.
    1355            0 :         customMeterDemand =
    1356            0 :             GetInstantMeterValue(state, demandMeterPtr_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec +
    1357            0 :             GetInstantMeterValue(state, demandMeterPtr_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
    1358              : 
    1359            0 :         remainingLoad = customMeterDemand;
    1360            0 :         loadCenterElectricLoad = remainingLoad;
    1361              : 
    1362            0 :         for (auto &g : elecGenCntrlObj) {
    1363            0 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1364              :                 // Set the Operation Flag
    1365            0 :                 g->onThisTimestep = true;
    1366              :                 // Set the electric generator load
    1367            0 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1368              : 
    1369              :                 // now handle EMS override
    1370            0 :                 if (g->eMSRequestOn) {
    1371            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1372            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1373            0 :                         g->onThisTimestep = true;
    1374              :                     } else {
    1375            0 :                         g->onThisTimestep = false;
    1376              :                     }
    1377              :                 }
    1378              :             } else {
    1379            0 :                 g->onThisTimestep = false;
    1380            0 :                 g->powerRequestThisTimestep = 0.0;
    1381              : 
    1382              :                 // now handle EMS override
    1383            0 :                 if (g->eMSRequestOn) {
    1384            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1385            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1386            0 :                         g->onThisTimestep = true;
    1387              :                     } else {
    1388            0 :                         g->onThisTimestep = false;
    1389              :                     }
    1390              :                 }
    1391              :             }
    1392              : 
    1393              :             // Get generator's actual electrical and thermal power outputs
    1394            0 :             g->simGeneratorGetPowerOutput(
    1395            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1396              : 
    1397            0 :             if (g->eMSRequestOn) {
    1398            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1399              :             } else {
    1400            0 :                 if (g->powerRequestThisTimestep > 0.0) {
    1401            0 :                     totalPowerRequest_ += g->maxPowerOut;
    1402            0 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1403              :                 }
    1404              :             }
    1405            0 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1406            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1407            0 :         } // end for
    1408            0 :         break;
    1409              :     }
    1410            0 :     case GeneratorOpScheme::ThermalFollow: {
    1411              :         // Turn thermal load into an electrical load for cogenerators controlled to follow heat loads
    1412            0 :         Real64 remainingThermalLoad = calcLoadCenterThermalLoad(state);
    1413            0 :         Real64 loadCenterThermalLoad = remainingThermalLoad;
    1414            0 :         for (auto &g : elecGenCntrlObj) {
    1415              : 
    1416            0 :             if (g->availSched->getCurrentVal() > 0.0 && remainingThermalLoad > 0.0) {
    1417              : 
    1418            0 :                 if (g->nominalThermElectRatio > 0.0) {
    1419            0 :                     remainingLoad = remainingThermalLoad / g->nominalThermElectRatio;
    1420            0 :                     g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1421            0 :                     g->onThisTimestep = true;
    1422              :                     // now handle EMS override
    1423            0 :                     if (g->eMSRequestOn) {
    1424            0 :                         g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1425            0 :                         if (g->powerRequestThisTimestep > 0.0) {
    1426            0 :                             g->onThisTimestep = true;
    1427              :                         } else {
    1428            0 :                             g->onThisTimestep = false;
    1429              :                         }
    1430              :                     }
    1431              :                 }
    1432              :             } else {
    1433            0 :                 g->onThisTimestep = false;
    1434            0 :                 g->powerRequestThisTimestep = 0.0;
    1435              :                 // now handle EMS override
    1436            0 :                 if (g->eMSRequestOn) {
    1437            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1438            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1439            0 :                         g->onThisTimestep = true;
    1440              :                     } else {
    1441            0 :                         g->onThisTimestep = false;
    1442              :                     }
    1443              :                 }
    1444              :             }
    1445              : 
    1446              :             // Get generator's actual electrical and thermal power outputs
    1447            0 :             g->simGeneratorGetPowerOutput(
    1448            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1449              : 
    1450            0 :             if (g->eMSRequestOn) {
    1451            0 :                 totalThermalPowerRequest_ += max(g->eMSPowerRequest, 0.0) * g->nominalThermElectRatio;
    1452            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1453              :             } else {
    1454            0 :                 if (totalThermalPowerRequest_ < loadCenterThermalLoad && g->powerRequestThisTimestep > 0.0) {
    1455            0 :                     Real64 excessThermalPowerRequest = totalThermalPowerRequest_ + g->maxPowerOut * g->nominalThermElectRatio - loadCenterThermalLoad;
    1456            0 :                     if (excessThermalPowerRequest < 0.0) {
    1457            0 :                         totalThermalPowerRequest_ += g->maxPowerOut * g->nominalThermElectRatio;
    1458            0 :                         totalPowerRequest_ += g->maxPowerOut;
    1459              :                     } else {
    1460            0 :                         totalThermalPowerRequest_ = loadCenterThermalLoad;
    1461            0 :                         if (g->nominalThermElectRatio > 0.0) {
    1462            0 :                             totalPowerRequest_ += g->maxPowerOut - (excessThermalPowerRequest / g->nominalThermElectRatio);
    1463              :                         }
    1464              :                     }
    1465              :                 }
    1466              :             }
    1467            0 :             remainingThermalLoad -= g->thermProdRate; // Update remaining load to be met
    1468              :             // by this load center
    1469            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1470            0 :         }
    1471            0 :         break;
    1472              :     }
    1473            0 :     case GeneratorOpScheme::ThermalFollowLimitElectrical: {
    1474              :         //  Turn a thermal load into an electrical load for cogenerators controlled to follow heat loads.
    1475              :         //  Add intitialization of RemainingThermalLoad as in the ThermalFollow operating scheme above.
    1476            0 :         Real64 remainingThermalLoad = calcLoadCenterThermalLoad(state);
    1477              :         // Total current electrical demand for the building is a secondary limit.
    1478            0 :         remainingLoad = remainingWholePowerDemand;
    1479            0 :         loadCenterElectricLoad = remainingWholePowerDemand;
    1480            0 :         Real64 loadCenterThermalLoad = remainingThermalLoad;
    1481            0 :         for (auto &g : elecGenCntrlObj) {
    1482            0 :             if ((g->availSched->getCurrentVal() > 0.0) && (remainingThermalLoad > 0.0) && (remainingLoad > 0.0)) {
    1483            0 :                 if (g->nominalThermElectRatio > 0.0) {
    1484            0 :                     remainingLoad = min(remainingWholePowerDemand, remainingThermalLoad / g->nominalThermElectRatio);
    1485            0 :                     g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1486            0 :                     g->onThisTimestep = true;
    1487              :                     // now handle EMS override
    1488            0 :                     if (g->eMSRequestOn) {
    1489            0 :                         g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1490            0 :                         if (g->powerRequestThisTimestep > 0.0) {
    1491            0 :                             g->onThisTimestep = true;
    1492              :                         } else {
    1493            0 :                             g->onThisTimestep = false;
    1494              :                         }
    1495              :                     }
    1496              :                 }
    1497              :             } else {
    1498            0 :                 g->onThisTimestep = false;
    1499            0 :                 g->powerRequestThisTimestep = 0.0;
    1500              :                 // now handle EMS override
    1501            0 :                 if (g->eMSRequestOn) {
    1502            0 :                     g->powerRequestThisTimestep = max(g->eMSPowerRequest, 0.0);
    1503            0 :                     if (g->powerRequestThisTimestep > 0.0) {
    1504            0 :                         g->onThisTimestep = true;
    1505              :                     } else {
    1506            0 :                         g->onThisTimestep = false;
    1507              :                     }
    1508              :                 }
    1509              :             }
    1510              :             // Get generator's actual electrical and thermal power outputs
    1511            0 :             g->simGeneratorGetPowerOutput(
    1512            0 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1513              : 
    1514            0 :             if (g->eMSRequestOn) {
    1515            0 :                 totalThermalPowerRequest_ += max(g->eMSPowerRequest, 0.0) * g->nominalThermElectRatio;
    1516            0 :                 totalPowerRequest_ += (max(g->eMSPowerRequest, 0.0));
    1517              :             } else {
    1518            0 :                 if (totalThermalPowerRequest_ < loadCenterThermalLoad && g->powerRequestThisTimestep > 0.0) {
    1519            0 :                     Real64 excessThermalPowerRequest = totalThermalPowerRequest_ + g->maxPowerOut * g->nominalThermElectRatio - loadCenterThermalLoad;
    1520            0 :                     if (excessThermalPowerRequest < 0.0) {
    1521            0 :                         totalThermalPowerRequest_ += g->maxPowerOut * g->nominalThermElectRatio;
    1522            0 :                         totalPowerRequest_ += g->maxPowerOut;
    1523              :                     } else {
    1524            0 :                         totalThermalPowerRequest_ = loadCenterThermalLoad;
    1525            0 :                         if (g->nominalThermElectRatio > 0.0) {
    1526            0 :                             totalPowerRequest_ += g->maxPowerOut - (excessThermalPowerRequest / g->nominalThermElectRatio);
    1527              :                         }
    1528              :                     }
    1529            0 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1530              :                 }
    1531              :             }
    1532            0 :             remainingThermalLoad -= g->thermProdRate; // Update remaining thermal load to
    1533              :             // be met by this load center
    1534            0 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining
    1535              :                                                            // electric load
    1536            0 :         }
    1537            0 :         break;
    1538              :     }
    1539            0 :     case GeneratorOpScheme::Invalid: {
    1540              :         // do nothing
    1541            0 :         break;
    1542              :     }
    1543            0 :     default:
    1544            0 :         assert(false);
    1545              :     } // end switch
    1546              : 
    1547              :     // sum up generator production
    1548         2235 :     genElectProdRate = 0.0;
    1549         2235 :     genElectricProd = 0.0;
    1550         4470 :     for (auto const &g : elecGenCntrlObj) {
    1551         2235 :         genElectProdRate += g->electProdRate;
    1552         2235 :         g->electricityProd = g->electProdRate * (state.dataHVACGlobal->TimeStepSysSec);
    1553         2235 :         genElectricProd += g->electricityProd;
    1554         2235 :     }
    1555         2235 : }
    1556              : 
    1557            0 : void ElectPowerLoadCenter::dispatchStorage(EnergyPlusData &state,
    1558              :                                            Real64 const originalFeedInRequest // whole building remaining electric demand for this load center
    1559              : )
    1560              : {
    1561              : 
    1562              :     // 1. resolve generator power rate into storage operation control volume, by buss type
    1563            0 :     switch (bussType) {
    1564            0 :     case ElectricBussType::Invalid:
    1565              :     case ElectricBussType::ACBuss:
    1566              :     case ElectricBussType::DCBussInverter: {
    1567              :         // do nothing, no storage to manage
    1568            0 :         break;
    1569              :     }
    1570            0 :     case ElectricBussType::ACBussStorage: {
    1571            0 :         storOpCVGenRate = genElectProdRate;
    1572            0 :         break;
    1573              :     }
    1574            0 :     case ElectricBussType::DCBussInverterDCStorage: {
    1575            0 :         storOpCVGenRate = genElectProdRate;
    1576            0 :         break;
    1577              :     }
    1578            0 :     case ElectricBussType::DCBussInverterACStorage: {
    1579              :         // TODO call inverter model here?
    1580            0 :         storOpCVGenRate = inverterObj->aCPowerOut();
    1581            0 :         break;
    1582              :     }
    1583            0 :     default:
    1584            0 :         assert(false);
    1585              :     } // end switch buss type
    1586              : 
    1587              :     // 2.  determine subpanel feed in and draw requests based on storage operation control scheme
    1588            0 :     Real64 subpanelFeedInRequest = 0.0;
    1589            0 :     Real64 subpanelDrawRequest = 0.0;
    1590            0 :     switch (storageScheme_) {
    1591            0 :     case StorageOpScheme::Invalid: {
    1592              :         // do nothing
    1593            0 :         break;
    1594              :     }
    1595            0 :     case StorageOpScheme::FacilityDemandStoreExcessOnSite: {
    1596            0 :         subpanelFeedInRequest = originalFeedInRequest; // legacy behavior, storage dispatched to meet building load
    1597            0 :         subpanelDrawRequest = 0.0;
    1598            0 :         break;
    1599              :     }
    1600            0 :     case StorageOpScheme::MeterDemandStoreExcessOnSite: {
    1601              :         // Get meter rate
    1602            0 :         subpanelFeedInRequest =
    1603            0 :             GetInstantMeterValue(state, trackStorageOpMeterIndex_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec +
    1604            0 :             GetInstantMeterValue(state, trackStorageOpMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
    1605            0 :         subpanelDrawRequest = 0.0;
    1606            0 :         break;
    1607              :     }
    1608            0 :     case StorageOpScheme::ChargeDischargeSchedules: {
    1609              :         // do not need to deal with subpanel rates here, charge or discharge is known from schedules and filled in below
    1610            0 :         break;
    1611              :     }
    1612            0 :     case StorageOpScheme::FacilityDemandLeveling: {
    1613            0 :         Real64 demandTarget = facilityDemandTarget_ * facilityDemandTargetModSched_->getCurrentVal();
    1614              :         // compare target to
    1615            0 :         Real64 deltaLoad = originalFeedInRequest - demandTarget;
    1616            0 :         if (deltaLoad >= 0.0) {
    1617              :             // subpanel should feed main panel
    1618            0 :             subpanelFeedInRequest = deltaLoad;
    1619            0 :             subpanelDrawRequest = 0.0;
    1620              :         } else {
    1621              :             // subpanel should draw from main panel
    1622            0 :             subpanelFeedInRequest = 0.0;
    1623            0 :             subpanelDrawRequest = std::abs(deltaLoad);
    1624              :         }
    1625            0 :         break;
    1626              :     }
    1627            0 :     default:
    1628            0 :         assert(false);
    1629              :     }
    1630              : 
    1631              :     // 3. adjust feed in and draw rates from subpanel to storage operation control volume
    1632            0 :     Real64 adjustedFeedInRequest = 0.0; // account for any inverter or transformer losses
    1633            0 :     Real64 adjustedDrawRequest = 0.0;   // account for any converer or transformer losses
    1634              : 
    1635            0 :     switch (bussType) {
    1636            0 :     case ElectricBussType::Invalid:
    1637              :     case ElectricBussType::ACBuss:
    1638              :     case ElectricBussType::DCBussInverter: {
    1639              :         // do nothing, no storage to manage
    1640            0 :         break;
    1641              :     }
    1642            0 :     case ElectricBussType::ACBussStorage:
    1643              :     case ElectricBussType::DCBussInverterACStorage: {
    1644            0 :         if (transformerObj == nullptr) {
    1645            0 :             adjustedFeedInRequest = subpanelFeedInRequest;
    1646            0 :             adjustedDrawRequest = subpanelDrawRequest;
    1647              :         } else {
    1648            0 :             adjustedFeedInRequest = subpanelFeedInRequest + transformerObj->getLossRateForOutputPower(state, subpanelFeedInRequest);
    1649            0 :             adjustedDrawRequest = subpanelDrawRequest - transformerObj->getLossRateForInputPower(state, subpanelDrawRequest);
    1650              :         }
    1651            0 :         break;
    1652              :     }
    1653            0 :     case ElectricBussType::DCBussInverterDCStorage: {
    1654              :         // can we get updated power conditioning losses here?
    1655            0 :         if (transformerObj == nullptr) {
    1656            0 :             adjustedFeedInRequest = subpanelFeedInRequest + inverterObj->getLossRateForOutputPower(state, subpanelFeedInRequest);
    1657            0 :             if (converterObj == nullptr) { // some operation schemes will never need a converter
    1658            0 :                 adjustedDrawRequest = subpanelDrawRequest;
    1659              :             } else {
    1660            0 :                 adjustedDrawRequest = subpanelDrawRequest - converterObj->getLossRateForInputPower(state, subpanelDrawRequest);
    1661              :             }
    1662              :         } else {
    1663            0 :             adjustedFeedInRequest = subpanelFeedInRequest + inverterObj->getLossRateForOutputPower(state, subpanelFeedInRequest) +
    1664            0 :                                     transformerObj->getLossRateForOutputPower(state, subpanelFeedInRequest);
    1665            0 :             if (converterObj == nullptr) {
    1666            0 :                 adjustedDrawRequest = subpanelDrawRequest - transformerObj->getLossRateForInputPower(state, subpanelDrawRequest);
    1667              :             } else {
    1668            0 :                 adjustedDrawRequest = subpanelDrawRequest - converterObj->getLossRateForInputPower(state, subpanelDrawRequest) -
    1669            0 :                                       transformerObj->getLossRateForInputPower(state, subpanelDrawRequest);
    1670              :             }
    1671              :         }
    1672            0 :         break;
    1673              :     }
    1674            0 :     default:
    1675            0 :         assert(false);
    1676              :     } // end switch buss type
    1677              : 
    1678            0 :     switch (storageScheme_) {
    1679            0 :     case StorageOpScheme::Invalid: {
    1680              :         // do nothing
    1681            0 :         break;
    1682              :     }
    1683            0 :     case StorageOpScheme::FacilityDemandStoreExcessOnSite: // these are both the same because adjusted feed in request has already accounted for the
    1684              :                                                            // difference
    1685              :     case StorageOpScheme::MeterDemandStoreExcessOnSite: {
    1686              :         // this is the legacy behavior but with more limits from storage control operation information
    1687              : 
    1688              :         // no draws from main panel
    1689            0 :         storOpCVDrawRate = 0.0;
    1690              : 
    1691            0 :         if (storOpCVGenRate < adjustedFeedInRequest) {
    1692              :             // draw from storage
    1693            0 :             storOpCVDischargeRate = adjustedFeedInRequest - storOpCVGenRate;
    1694            0 :             storOpCVChargeRate = 0.0;
    1695            0 :             storOpIsDischarging = true;
    1696            0 :             storOpIsCharging = false;
    1697              : 
    1698            0 :         } else if (storOpCVGenRate > adjustedFeedInRequest) {
    1699              :             // add to storage
    1700            0 :             storOpCVDischargeRate = 0.0;
    1701            0 :             storOpCVChargeRate = storOpCVGenRate - adjustedFeedInRequest;
    1702            0 :             storOpIsCharging = true;
    1703            0 :             storOpIsDischarging = false;
    1704              : 
    1705            0 :         } else if (storOpCVGenRate == adjustedFeedInRequest) {
    1706              :             // do nothing
    1707            0 :             storOpCVDischargeRate = 0.0;
    1708            0 :             storOpCVChargeRate = 0.0;
    1709            0 :             storOpIsCharging = false;
    1710            0 :             storOpIsDischarging = false;
    1711              :         }
    1712            0 :         break;
    1713              :     }
    1714              : 
    1715            0 :     case StorageOpScheme::ChargeDischargeSchedules: {
    1716            0 :         storOpCVChargeRate = designStorageChargePower_ * storageChargeModSched_->getCurrentVal();
    1717            0 :         storOpCVDischargeRate = designStorageDischargePower_ * storageDischargeModSched_->getCurrentVal();
    1718            0 :         Real64 genAndStorSum = storOpCVGenRate + storOpCVDischargeRate - storOpCVChargeRate;
    1719            0 :         if (genAndStorSum >= 0.0) { // power to feed toward main panel
    1720            0 :             storOpCVDrawRate = 0.0;
    1721            0 :             storOpCVFeedInRate = genAndStorSum;
    1722              :         } else { // shortfall, will need to draw from main panel (e.g. for grid charging)
    1723            0 :             storOpCVFeedInRate = 0.0;
    1724            0 :             storOpCVDrawRate = std::abs(genAndStorSum);
    1725              :         }
    1726            0 :         if (storOpCVChargeRate > 0.0) {
    1727            0 :             storOpIsCharging = true;
    1728              :         } else {
    1729            0 :             storOpIsCharging = false;
    1730              :         }
    1731            0 :         if (storOpCVDischargeRate > 0.0) {
    1732            0 :             storOpIsDischarging = true;
    1733              :         } else {
    1734            0 :             storOpIsDischarging = false;
    1735              :         }
    1736            0 :         break;
    1737              :     }
    1738            0 :     case StorageOpScheme::FacilityDemandLeveling: {
    1739              : 
    1740            0 :         if (adjustedDrawRequest > 0.0) { // the only reason to draw instead of feed is to charge storage
    1741            0 :             storOpCVFeedInRate = 0.0;
    1742            0 :             storOpCVDrawRate = adjustedDrawRequest;
    1743            0 :             storOpCVChargeRate = storOpCVDrawRate + storOpCVGenRate;
    1744            0 :             storOpCVDischargeRate = 0.0;
    1745            0 :             storOpIsCharging = true;
    1746            0 :             storOpIsDischarging = false;
    1747              :         }
    1748            0 :         if (adjustedFeedInRequest > 0.0) {
    1749            0 :             storOpCVDrawRate = 0.0;
    1750            0 :             storOpCVFeedInRate = adjustedFeedInRequest;
    1751            0 :             if (storOpCVGenRate < adjustedFeedInRequest) {
    1752              :                 // draw from storage
    1753            0 :                 storOpCVDischargeRate = adjustedFeedInRequest - storOpCVGenRate;
    1754            0 :                 storOpCVChargeRate = 0.0;
    1755            0 :                 storOpIsDischarging = true;
    1756            0 :                 storOpIsCharging = false;
    1757              : 
    1758            0 :             } else if (storOpCVGenRate > adjustedFeedInRequest) {
    1759              :                 // add to storage
    1760            0 :                 storOpCVDischargeRate = 0.0;
    1761            0 :                 storOpCVChargeRate = storOpCVGenRate - adjustedFeedInRequest;
    1762            0 :                 storOpIsCharging = true;
    1763            0 :                 storOpIsDischarging = false;
    1764              : 
    1765            0 :             } else if (storOpCVGenRate == adjustedFeedInRequest) {
    1766              :                 // do nothing
    1767            0 :                 storOpCVDischargeRate = 0.0;
    1768            0 :                 storOpCVChargeRate = 0.0;
    1769            0 :                 storOpIsCharging = false;
    1770            0 :                 storOpIsDischarging = false;
    1771              :             }
    1772              :         }
    1773            0 :         break;
    1774              :     }
    1775            0 :     default:
    1776              :         assert(true);
    1777              :     }
    1778              : 
    1779              :     // handle EMS overrides
    1780            0 :     if (eMSOverridePelFromStorage_ || eMSOverridePelIntoStorage_) {
    1781            0 :         if (eMSOverridePelFromStorage_ && !eMSOverridePelIntoStorage_) {
    1782              :             // EMS is calling for specific discharge rate
    1783            0 :             storOpCVDischargeRate = max(eMSValuePelFromStorage_, 0.0);
    1784            0 :             storOpCVChargeRate = 0.0;
    1785            0 :             storOpIsDischarging = true;
    1786            0 :             storOpIsCharging = false;
    1787            0 :         } else if (!eMSOverridePelFromStorage_ && eMSOverridePelIntoStorage_) {
    1788              :             // EMS is calling for specific charge rate
    1789            0 :             storOpCVChargeRate = max(eMSValuePelIntoStorage_, 0.0);
    1790            0 :             storOpCVDischargeRate = 0.0;
    1791            0 :             storOpIsDischarging = false;
    1792            0 :             storOpIsCharging = true;
    1793            0 :         } else if (eMSOverridePelFromStorage_ && eMSOverridePelIntoStorage_) {
    1794              :             // EMS is asking to override both
    1795            0 :             if (eMSValuePelIntoStorage_ > eMSValuePelFromStorage_) {
    1796            0 :                 storOpCVChargeRate = eMSValuePelIntoStorage_ - eMSValuePelFromStorage_;
    1797            0 :                 storOpCVDischargeRate = 0.0;
    1798            0 :                 storOpIsDischarging = false;
    1799            0 :                 storOpIsCharging = true;
    1800            0 :             } else if (eMSValuePelIntoStorage_ < eMSValuePelFromStorage_) {
    1801            0 :                 storOpCVDischargeRate = eMSValuePelFromStorage_ - eMSValuePelIntoStorage_;
    1802            0 :                 storOpCVChargeRate = 0.0;
    1803            0 :                 storOpIsDischarging = true;
    1804            0 :                 storOpIsCharging = false;
    1805              :             } else { // they equal just hold
    1806            0 :                 storOpCVDischargeRate = 0.0;
    1807            0 :                 storOpCVChargeRate = 0.0;
    1808            0 :                 storOpIsDischarging = false;
    1809            0 :                 storOpIsCharging = false;
    1810              :             }
    1811              :         }
    1812              :     }
    1813              : 
    1814              :     // check against the controller limits
    1815            0 :     if (designStorageChargePowerWasSet_) {
    1816            0 :         storOpCVChargeRate = min(storOpCVChargeRate, designStorageChargePower_);
    1817              :     }
    1818            0 :     if (designStorageDischargePowerWasSet_) {
    1819            0 :         storOpCVDischargeRate = min(storOpCVDischargeRate, designStorageDischargePower_);
    1820              :     }
    1821              : 
    1822              :     // dispatch final request to storage device, calculate, update, and report storage device, passing what controller wants for SOC limits
    1823              : 
    1824            0 :     storageObj->simulate(
    1825            0 :         state, storOpCVChargeRate, storOpCVDischargeRate, storOpIsCharging, storOpIsDischarging, maxStorageSOCFraction_, minStorageSOCFraction_);
    1826              : 
    1827              :     // rebalance with final charge and discharge rates
    1828            0 :     Real64 genAndStorSum = storOpCVGenRate + storOpCVDischargeRate - storOpCVChargeRate;
    1829            0 :     if (genAndStorSum >= 0.0) { // power to feed toward main panel
    1830            0 :         storOpCVDrawRate = 0.0;
    1831            0 :         storOpCVFeedInRate = genAndStorSum;
    1832              :     } else { // shortfall, will need to draw from main panel (e.g. for grid charging)
    1833            0 :         storOpCVFeedInRate = 0.0;
    1834            0 :         storOpCVDrawRate = std::abs(genAndStorSum);
    1835              :     }
    1836            0 : }
    1837              : 
    1838          100 : void ElectPowerLoadCenter::setupLoadCenterMeterIndices(EnergyPlusData &state)
    1839              : {
    1840          100 :     demandMeterPtr_ = EnergyPlus::GetMeterIndex(state, demandMeterName_);
    1841          100 :     if ((demandMeterPtr_ == 0) && (genOperationScheme_ == GeneratorOpScheme::TrackMeter)) { // throw error
    1842            0 :         ShowFatalError(
    1843              :             state,
    1844            0 :             format("ElectPowerLoadCenter::setupLoadCenterMeterIndices  Did not find Meter named: {} in ElectricLoadCenter:Distribution named {}",
    1845            0 :                    demandMeterName_,
    1846            0 :                    name_));
    1847              :     }
    1848              : 
    1849          100 :     if (storageScheme_ == StorageOpScheme::MeterDemandStoreExcessOnSite) {
    1850            0 :         trackStorageOpMeterIndex_ = EnergyPlus::GetMeterIndex(state, trackSorageOpMeterName_);
    1851            0 :         if (trackStorageOpMeterIndex_ == 0) { //
    1852            0 :             ShowFatalError(
    1853              :                 state,
    1854            0 :                 format("ElectPowerLoadCenter::setupLoadCenterMeterIndices  Did not find Meter named: {} in ElectricLoadCenter:Distribution named {}",
    1855            0 :                        trackSorageOpMeterName_,
    1856            0 :                        name_));
    1857              :         }
    1858              :     }
    1859          100 : }
    1860              : 
    1861          375 : void ElectPowerLoadCenter::reinitAtBeginEnvironment()
    1862              : {
    1863          375 :     genElectricProd = 0.0;
    1864          375 :     genElectProdRate = 0.0;
    1865          375 :     thermalProd = 0.0;
    1866          375 :     thermalProdRate = 0.0;
    1867          375 :     totalPowerRequest_ = 0.0;
    1868          375 :     totalThermalPowerRequest_ = 0.0;
    1869          375 :     subpanelFeedInRate = 0.0;
    1870          375 :     subpanelDrawRate = 0.0;
    1871          375 :     storOpCVDrawRate = 0.0;
    1872          375 :     storOpCVFeedInRate = 0.0;
    1873          375 :     storOpCVChargeRate = 0.0;
    1874          375 :     storOpCVDischargeRate = 0.0;
    1875              : 
    1876          375 :     if (generatorsPresent_ && numGenerators > 0) {
    1877            4 :         for (auto &g : elecGenCntrlObj) {
    1878            2 :             g->reinitAtBeginEnvironment();
    1879            2 :         }
    1880              :     }
    1881              : 
    1882          375 :     if (transformerObj != nullptr) {
    1883            0 :         transformerObj->reinitAtBeginEnvironment();
    1884              :     }
    1885              : 
    1886          375 :     if (storageObj != nullptr) {
    1887            0 :         storageObj->reinitAtBeginEnvironment();
    1888              :     }
    1889              : 
    1890          375 :     if (inverterObj != nullptr) {
    1891            0 :         inverterObj->reinitAtBeginEnvironment();
    1892              :     }
    1893              : 
    1894          375 :     if (converterObj != nullptr) {
    1895            0 :         converterObj->reinitAtBeginEnvironment();
    1896              :     }
    1897          375 : }
    1898              : 
    1899          302 : void ElectPowerLoadCenter::reinitZoneGainsAtBeginEnvironment()
    1900              : {
    1901          302 :     if (transformerObj != nullptr) {
    1902            0 :         transformerObj->reinitZoneGainsAtBeginEnvironment();
    1903              :     }
    1904              : 
    1905          302 :     if (storageObj != nullptr) {
    1906            0 :         storageObj->reinitZoneGainsAtBeginEnvironment();
    1907              :     }
    1908              : 
    1909          302 :     if (inverterObj != nullptr) {
    1910            0 :         inverterObj->reinitZoneGainsAtBeginEnvironment();
    1911              :     }
    1912              : 
    1913          302 :     if (converterObj != nullptr) {
    1914            0 :         converterObj->reinitZoneGainsAtBeginEnvironment();
    1915              :     }
    1916          302 : }
    1917              : 
    1918            1 : std::string const &ElectPowerLoadCenter::generatorListName() const
    1919              : {
    1920            1 :     return generatorListName_;
    1921              : }
    1922              : 
    1923      1089398 : void ElectPowerLoadCenter::updateLoadCenterGeneratorRecords(EnergyPlusData &state)
    1924              : {
    1925              : 
    1926      1089398 :     switch (bussType) {
    1927         4471 :     case ElectricBussType::ACBuss: {
    1928         4471 :         genElectProdRate = 0.0;
    1929         4471 :         genElectricProd = 0.0;
    1930         8943 :         for (auto const &gc : elecGenCntrlObj) {
    1931         4472 :             genElectProdRate += gc->electProdRate;
    1932         4472 :             genElectricProd += gc->electricityProd;
    1933         4471 :         }
    1934              :         // no inverter, no storage, so generator production equals subpanel feed in
    1935         4471 :         subpanelFeedInRate = genElectProdRate;
    1936         4471 :         if (transformerObj != nullptr) {
    1937            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, genElectProdRate);
    1938              :         }
    1939         4471 :         subpanelDrawRate = 0.0;
    1940              : 
    1941         4471 :         break;
    1942              :     }
    1943            1 :     case ElectricBussType::ACBussStorage: {
    1944            1 :         genElectProdRate = 0.0;
    1945            1 :         genElectricProd = 0.0;
    1946            3 :         for (auto const &gc : elecGenCntrlObj) {
    1947            2 :             genElectProdRate += gc->electProdRate;
    1948            2 :             genElectricProd += gc->electricityProd;
    1949            1 :         }
    1950            1 :         if (storageObj != nullptr) {
    1951            1 :             subpanelFeedInRate = genElectProdRate + storOpCVDischargeRate - storOpCVChargeRate;
    1952              :         } else {
    1953            0 :             subpanelFeedInRate = genElectProdRate;
    1954              :         }
    1955            1 :         if (transformerObj != nullptr) {
    1956            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1957              :         }
    1958            1 :         subpanelDrawRate = 0.0;
    1959            1 :         break;
    1960              :     }
    1961            2 :     case ElectricBussType::DCBussInverter: {
    1962            2 :         genElectProdRate = 0.0;
    1963            2 :         genElectricProd = 0.0;
    1964            6 :         for (auto const &gc : elecGenCntrlObj) {
    1965            4 :             genElectProdRate += gc->electProdRate;
    1966            4 :             genElectricProd += gc->electricityProd;
    1967            2 :         }
    1968              : 
    1969            2 :         if (inverterObj != nullptr) {
    1970            2 :             subpanelFeedInRate = inverterObj->aCPowerOut();
    1971              :         }
    1972            2 :         if (transformerObj != nullptr) {
    1973            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1974              :         }
    1975            2 :         subpanelDrawRate = 0.0;
    1976            2 :         break;
    1977              :     }
    1978              : 
    1979            2 :     case ElectricBussType::DCBussInverterDCStorage: {
    1980            2 :         genElectProdRate = 0.0;
    1981            2 :         genElectricProd = 0.0;
    1982            6 :         for (auto const &gc : elecGenCntrlObj) {
    1983            4 :             genElectProdRate += gc->electProdRate;
    1984            4 :             genElectricProd += gc->electricityProd;
    1985            2 :         }
    1986            2 :         if (inverterObj != nullptr) {
    1987            2 :             subpanelFeedInRate = inverterObj->aCPowerOut();
    1988              :         }
    1989              : 
    1990            2 :         if (converterObj != nullptr) {
    1991            0 :             subpanelDrawRate = converterObj->aCPowerIn();
    1992              :         }
    1993            2 :         if (transformerObj != nullptr) {
    1994            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1995            0 :             subpanelDrawRate += transformerObj->getLossRateForOutputPower(state, subpanelDrawRate);
    1996              :         }
    1997            2 :         break;
    1998              :     }
    1999            2 :     case ElectricBussType::DCBussInverterACStorage: {
    2000            2 :         genElectProdRate = 0.0;
    2001            2 :         genElectricProd = 0.0;
    2002            6 :         for (auto const &gc : elecGenCntrlObj) {
    2003            4 :             genElectProdRate += gc->electProdRate;
    2004            4 :             genElectricProd += gc->electricityProd;
    2005            2 :         }
    2006            2 :         if (inverterObj != nullptr && storagePresent_) {
    2007            2 :             subpanelFeedInRate = inverterObj->aCPowerOut() + storOpCVDischargeRate - storOpCVChargeRate;
    2008              :         }
    2009              : 
    2010            2 :         subpanelDrawRate = storOpCVDrawRate; // no converter for AC storage
    2011            2 :         if (transformerObj != nullptr) {
    2012            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    2013            0 :             subpanelDrawRate += transformerObj->getLossRateForOutputPower(state, subpanelDrawRate);
    2014              :         }
    2015            2 :         break;
    2016              :     }
    2017      1084920 :     case ElectricBussType::Invalid: {
    2018              :         // do nothing
    2019      1084920 :         break;
    2020              :     }
    2021            0 :     default:
    2022            0 :         assert(false);
    2023              :     } // end switch
    2024      1089398 :     thermalProdRate = 0.0;
    2025      1089398 :     thermalProd = 0.0;
    2026      1093884 :     for (auto const &gc : elecGenCntrlObj) {
    2027         4486 :         thermalProdRate += gc->thermProdRate;
    2028         4486 :         thermalProd += gc->thermalProd;
    2029      1089398 :     }
    2030      1089398 : }
    2031              : 
    2032            0 : Real64 ElectPowerLoadCenter::calcLoadCenterThermalLoad(EnergyPlusData &state)
    2033              : {
    2034            0 :     if (myCoGenSetupFlag_) {
    2035            0 :         for (auto &g : elecGenCntrlObj) {
    2036            0 :             bool plantNotFound = false;
    2037            0 :             PlantUtilities::ScanPlantLoopsForObject(state, g->compPlantName, g->compPlantType, g->cogenLocation, plantNotFound, _, _, _, _, _);
    2038            0 :             if (!plantNotFound) {
    2039            0 :                 g->plantInfoFound = true;
    2040              :             }
    2041            0 :         }
    2042            0 :         myCoGenSetupFlag_ = false;
    2043              :     } // cogen setup
    2044              : 
    2045              :     // sum up "MyLoad" for all generators on this load center from plant structure
    2046            0 :     Real64 thermalLoad = 0.0;
    2047            0 :     for (auto &g : elecGenCntrlObj) {
    2048            0 :         if (g->plantInfoFound) {
    2049            0 :             thermalLoad += state.dataPlnt->PlantLoop(g->cogenLocation.loopNum)
    2050            0 :                                .LoopSide(g->cogenLocation.loopSideNum)
    2051            0 :                                .Branch(g->cogenLocation.branchNum)
    2052            0 :                                .Comp(g->cogenLocation.compNum)
    2053            0 :                                .MyLoad;
    2054              :         }
    2055            0 :     }
    2056            0 :     return thermalLoad;
    2057              : }
    2058              : 
    2059              : // TODO: Constructors should not do this much work
    2060           21 : GeneratorController::GeneratorController(EnergyPlusData &state,
    2061              :                                          std::string const &objectName,
    2062              :                                          std::string const &objectType,
    2063              :                                          Real64 ratedElecPowerOutput,
    2064              :                                          std::string const &availSchedName,
    2065           21 :                                          Real64 thermalToElectRatio)
    2066           42 :     : generatorType(GeneratorType::Invalid), compPlantType(DataPlant::PlantEquipmentType::Invalid), generatorIndex(0), maxPowerOut(0.0),
    2067           21 :       powerRequestThisTimestep(0.0), onThisTimestep(false), eMSPowerRequest(0.0), eMSRequestOn(false), plantInfoFound(false),
    2068           42 :       cogenLocation(PlantLocation(0, DataPlant::LoopSideLocation::Invalid, 0, 0)), nominalThermElectRatio(0.0), dCElectricityProd(0.0),
    2069           21 :       dCElectProdRate(0.0), electricityProd(0.0), electProdRate(0.0), thermalProd(0.0), thermProdRate(0.0), pvwattsGenerator(nullptr),
    2070           21 :       errCountNegElectProd_(0)
    2071              : {
    2072              : 
    2073              :     static constexpr std::string_view routineName = "GeneratorController constructor ";
    2074              : 
    2075           21 :     auto &s_ipsc = state.dataIPShortCut;
    2076              : 
    2077           21 :     ErrorObjectHeader eoh{routineName, objectType, objectName};
    2078              : 
    2079           21 :     name = objectName;
    2080              : 
    2081           21 :     generatorType = static_cast<GeneratorType>(getEnumValue(generatorTypeNamesUC, Util::makeUPPER(objectType)));
    2082           21 :     switch (generatorType) {
    2083            6 :     case GeneratorType::ICEngine: {
    2084            6 :         compPlantType = DataPlant::PlantEquipmentType::Generator_ICEngine;
    2085            6 :         compPlantName = name;
    2086            6 :         break;
    2087              :     }
    2088            0 :     case GeneratorType::CombTurbine: {
    2089            0 :         compPlantType = DataPlant::PlantEquipmentType::Generator_CTurbine;
    2090            0 :         compPlantName = name;
    2091            0 :         break;
    2092              :     }
    2093            0 :     case GeneratorType::Microturbine: {
    2094            0 :         compPlantType = DataPlant::PlantEquipmentType::Generator_MicroTurbine;
    2095            0 :         compPlantName = name;
    2096            0 :         break;
    2097              :     }
    2098            4 :     case GeneratorType::PV: {
    2099            4 :         compPlantType = DataPlant::PlantEquipmentType::PVTSolarCollectorFlatPlate;
    2100            4 :         compPlantName = name;
    2101            4 :         break;
    2102              :     }
    2103            4 :     case GeneratorType::PVWatts: {
    2104            4 :         compPlantType = DataPlant::PlantEquipmentType::Invalid;
    2105              : 
    2106            4 :         int ObjNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "Generator:PVWatts", Util::makeUPPER(objectName));
    2107            4 :         assert(ObjNum >= 0);
    2108            4 :         if (ObjNum == 0) {
    2109            0 :             ShowFatalError(state, format("Cannot find Generator:PVWatts {}", objectName));
    2110              :         }
    2111            4 :         pvwattsGenerator = PVWatts::PVWattsGenerator::createFromIdfObj(state, ObjNum);
    2112            4 :         pvwattsGenerator->setupOutputVariables(state);
    2113            4 :         break;
    2114              :     }
    2115            1 :     case GeneratorType::FuelCell: {
    2116              :         // fuel cell has two possible plant component types, stack cooler and exhaust gas HX.
    2117              :         // exhaust gas HX is required and it assumed that it has more thermal capacity and is used for control
    2118            1 :         compPlantType = DataPlant::PlantEquipmentType::Generator_FCExhaust;
    2119              :         // and the name of plant component is not the same as the generator because of child object references, so fetch that name
    2120            1 :         auto *thisFC = FuelCellElectricGenerator::FCDataStruct::factory(state, name);
    2121            1 :         compPlantName = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->ExhaustHX.Name;
    2122            1 :         break;
    2123              :     }
    2124            0 :     case GeneratorType::MicroCHP: {
    2125            0 :         compPlantType = DataPlant::PlantEquipmentType::Generator_MicroCHP;
    2126            0 :         compPlantName = name;
    2127            0 :         break;
    2128              :     }
    2129            6 :     case GeneratorType::WindTurbine: {
    2130            6 :         compPlantType = DataPlant::PlantEquipmentType::Invalid;
    2131            6 :         break;
    2132              :     }
    2133            0 :     default: {
    2134            0 :         ShowSevereError(state, format("{}{} invalid entry.", routineName, s_ipsc->cCurrentModuleObject));
    2135            0 :         ShowContinueError(state, format("Invalid {} associated with generator = {}", objectType, objectName));
    2136            0 :         break;
    2137              :     }
    2138              :     }
    2139              : 
    2140           21 :     if (availSchedName.empty()) {
    2141            5 :         availSched = Sched::GetScheduleAlwaysOn(state);
    2142           16 :     } else if ((availSched = Sched::GetSchedule(state, availSchedName)) == nullptr) {
    2143            0 :         ShowSevereItemNotFound(state, eoh, "Availability Schedule Name", availSchedName);
    2144           16 :     } else if (generatorType == GeneratorType::PVWatts) {
    2145            1 :         ShowWarningCustom(state, eoh, "Availability Schedule will be ignored (runs all the time).");
    2146           15 :     } else if (generatorType == GeneratorType::PV) {
    2147              :         // It should only warn if Performance type is SimplePV (DataPhotovoltaics::iSimplePVModel).
    2148              :         // Except you need GetPVInput to have run already etc
    2149              :         // Note: you can't use s_ipsc->cAlphaArgs etc or it'll override what will still need to be processed in
    2150              :         // ElectPowerLoadCenter::ElectPowerLoadCenter after this function is called
    2151            2 :         int PVNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, objectType, Util::makeUPPER(objectName));
    2152              :         int NumAlphas; // Number of PV Array parameter alpha names being passed
    2153              :         int NumNums;   // Number of PV Array numeric parameters are being passed
    2154              :         int IOStat;
    2155            2 :         Array1D_string Alphas(5);   // Alpha items for object
    2156            2 :         Array1D<Real64> Numbers(2); // Numeric items for object
    2157            2 :         state.dataInputProcessing->inputProcessor->getObjectItem(state, objectType, PVNum, Alphas, NumAlphas, Numbers, NumNums, IOStat);
    2158            2 :         if (Util::SameString(Alphas(3), "PhotovoltaicPerformance:Simple")) {
    2159            2 :             ShowWarningCustom(state, eoh, "Availability Schedule will be ignored (runs all the time).");
    2160            6 :             ShowContinueError(state, "To limit this Generator:Photovoltaic's output, please use the Inverter's availability schedule instead.");
    2161              :         }
    2162            2 :     }
    2163              : 
    2164           21 :     maxPowerOut = ratedElecPowerOutput, nominalThermElectRatio = thermalToElectRatio;
    2165              : 
    2166           42 :     SetupOutputVariable(state,
    2167              :                         "Generator Requested Electricity Rate",
    2168              :                         Constant::Units::W,
    2169           21 :                         powerRequestThisTimestep,
    2170              :                         OutputProcessor::TimeStepType::System,
    2171              :                         OutputProcessor::StoreType::Average,
    2172              :                         objectName);
    2173           21 :     if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
    2174            0 :         SetupEMSInternalVariable(state, "Generator Nominal Maximum Power", objectName, "[W]", maxPowerOut);
    2175            0 :         SetupEMSInternalVariable(state, "Generator Nominal Thermal To Electric Ratio", objectName, "[ratio]", nominalThermElectRatio);
    2176            0 :         SetupEMSActuator(state, "On-Site Generator Control", objectName, "Requested Power", "[W]", eMSRequestOn, eMSPowerRequest);
    2177              :     }
    2178           21 : }
    2179              : 
    2180            2 : void GeneratorController::reinitAtBeginEnvironment()
    2181              : {
    2182            2 :     onThisTimestep = false;
    2183            2 :     dCElectricityProd = 0.0;
    2184            2 :     dCElectProdRate = 0.0;
    2185            2 :     electricityProd = 0.0;
    2186            2 :     electProdRate = 0.0;
    2187            2 :     thermalProd = 0.0;
    2188            2 :     thermProdRate = 0.0;
    2189            2 : }
    2190              : 
    2191         2235 : void GeneratorController::simGeneratorGetPowerOutput(EnergyPlusData &state,
    2192              :                                                      bool const runFlag,
    2193              :                                                      Real64 const myElecLoadRequest,
    2194              :                                                      bool const FirstHVACIteration, // Unused 2010 JANUARY
    2195              :                                                      Real64 &electricPowerOutput,   // Actual generator electric power output
    2196              :                                                      Real64 &thermalPowerOutput     // Actual generator thermal power output
    2197              : )
    2198              : {
    2199              :     // Select and call models and also collect results for load center power conditioning and reporting
    2200         2235 :     switch (generatorType) {
    2201            0 :     case GeneratorType::ICEngine: {
    2202              : 
    2203            0 :         auto *thisICE = ICEngineElectricGenerator::ICEngineGeneratorSpecs::factory(state, name);
    2204              : 
    2205              :         // dummy vars
    2206            0 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2207            0 :         Real64 tempLoad = myElecLoadRequest;
    2208              : 
    2209              :         // simulate
    2210            0 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->InitICEngineGenerators(state, runFlag, FirstHVACIteration);
    2211            0 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->CalcICEngineGeneratorModel(state, runFlag, tempLoad);
    2212            0 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->update(state);
    2213            0 :         electProdRate = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->ElecPowerGenerated;
    2214            0 :         electricityProd = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->ElecEnergyGenerated;
    2215            0 :         thermProdRate = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->QTotalHeatRecovered;
    2216            0 :         thermalProd = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->TotalHeatEnergyRec;
    2217            0 :         electricPowerOutput = electProdRate;
    2218            0 :         thermalPowerOutput = thermProdRate;
    2219            0 :         break;
    2220              :     }
    2221            0 :     case GeneratorType::CombTurbine: {
    2222              : 
    2223            0 :         auto *thisCTE = CTElectricGenerator::CTGeneratorData::factory(state, name);
    2224              :         // dummy vars
    2225            0 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2226            0 :         Real64 tempLoad = myElecLoadRequest;
    2227              : 
    2228              :         // simulate
    2229            0 :         thisCTE->simulate(state, L, FirstHVACIteration, tempLoad, runFlag);
    2230            0 :         dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->InitCTGenerators(state, runFlag, FirstHVACIteration);
    2231            0 :         dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->CalcCTGeneratorModel(state, runFlag, tempLoad, FirstHVACIteration);
    2232            0 :         electProdRate = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->ElecPowerGenerated;
    2233            0 :         electricityProd = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->ElecEnergyGenerated;
    2234            0 :         thermProdRate = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->QTotalHeatRecovered;
    2235            0 :         thermalProd = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->TotalHeatEnergyRec;
    2236            0 :         electricPowerOutput = electProdRate;
    2237            0 :         thermalPowerOutput = thermProdRate;
    2238            0 :         break;
    2239              :     }
    2240            0 :     case GeneratorType::PV: {
    2241            0 :         Photovoltaics::SimPVGenerator(state, GeneratorType::PV, name, generatorIndex, runFlag, myElecLoadRequest);
    2242            0 :         Photovoltaics::GetPVGeneratorResults(
    2243            0 :             state, GeneratorType::PV, generatorIndex, dCElectProdRate, dCElectricityProd, thermProdRate, thermalProd);
    2244            0 :         electricPowerOutput = dCElectProdRate;
    2245            0 :         thermalPowerOutput = thermProdRate;
    2246            0 :         break;
    2247              :     }
    2248            0 :     case GeneratorType::PVWatts: {
    2249            0 :         pvwattsGenerator->calc(state);
    2250            0 :         pvwattsGenerator->getResults(dCElectProdRate, dCElectricityProd, thermProdRate, thermalProd);
    2251            0 :         electricPowerOutput = dCElectProdRate;
    2252            0 :         thermalPowerOutput = thermProdRate;
    2253            0 :         break;
    2254              :     }
    2255         2235 :     case GeneratorType::FuelCell: {
    2256         2235 :         auto *thisFC = FuelCellElectricGenerator::FCDataStruct::factory(state, name);
    2257         2235 :         dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->SimFuelCellGenerator(state, runFlag, myElecLoadRequest, FirstHVACIteration);
    2258         2235 :         electProdRate = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.ACPowerGen;
    2259         2235 :         electricityProd = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.ACEnergyGen;
    2260         2235 :         thermProdRate = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.qHX;
    2261         2235 :         thermalProd = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.HXenergy;
    2262         2235 :         electricPowerOutput = electProdRate;
    2263         2235 :         thermalPowerOutput = thermProdRate;
    2264         2235 :         break;
    2265              :     }
    2266            0 :     case GeneratorType::MicroCHP: {
    2267            0 :         auto *thisMCHP = MicroCHPElectricGenerator::MicroCHPDataStruct::factory(state, name);
    2268              : 
    2269              :         // simulate
    2270            0 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->InitMicroCHPNoNormalizeGenerators(state);
    2271              : 
    2272            0 :         if (!state.dataPlnt->PlantFirstSizeCompleted) {
    2273            0 :             break;
    2274              :         }
    2275              : 
    2276            0 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->CalcMicroCHPNoNormalizeGeneratorModel(
    2277              :             state, runFlag, false, myElecLoadRequest, DataPrecisionGlobals::constant_zero);
    2278            0 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->CalcUpdateHeatRecovery(state);
    2279            0 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->UpdateMicroCHPGeneratorRecords(state);
    2280              : 
    2281            0 :         electProdRate = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.ACPowerGen;
    2282            0 :         electricityProd = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.ACEnergyGen;
    2283            0 :         thermProdRate = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.QdotHR;
    2284            0 :         thermalProd = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.TotalHeatEnergyRec;
    2285            0 :         electricPowerOutput = electProdRate;
    2286            0 :         thermalPowerOutput = thermProdRate;
    2287            0 :         break;
    2288              :     }
    2289            0 :     case GeneratorType::Microturbine: {
    2290            0 :         auto *thisMTG = MicroturbineElectricGenerator::MTGeneratorSpecs::factory(state, name);
    2291              : 
    2292              :         // dummy vars
    2293            0 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2294            0 :         Real64 tempLoad = myElecLoadRequest;
    2295              : 
    2296              :         // simulate
    2297            0 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->InitMTGenerators(state, runFlag, tempLoad, FirstHVACIteration);
    2298            0 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->CalcMTGeneratorModel(state, runFlag, tempLoad);
    2299            0 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->UpdateMTGeneratorRecords(state);
    2300            0 :         electProdRate = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->ElecPowerGenerated;
    2301            0 :         electricityProd = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->EnergyGen;
    2302            0 :         thermProdRate = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->QHeatRecovered;
    2303            0 :         thermalProd = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->ExhaustEnergyRec;
    2304            0 :         electricPowerOutput = electProdRate;
    2305            0 :         thermalPowerOutput = thermProdRate;
    2306            0 :         break;
    2307              :     }
    2308            0 :     case GeneratorType::WindTurbine: {
    2309            0 :         WindTurbine::SimWindTurbine(state, GeneratorType::WindTurbine, name, generatorIndex, runFlag, myElecLoadRequest);
    2310            0 :         WindTurbine::GetWTGeneratorResults(
    2311            0 :             state, GeneratorType::WindTurbine, generatorIndex, electProdRate, electricityProd, thermProdRate, thermalProd);
    2312            0 :         electricPowerOutput = electProdRate;
    2313            0 :         thermalPowerOutput = thermProdRate;
    2314            0 :         break;
    2315              :     }
    2316            0 :     case GeneratorType::Invalid:
    2317              :     case GeneratorType::Num: {
    2318              :         // do nothing
    2319            0 :         break;
    2320              :     }
    2321              :     } // end switch
    2322              : 
    2323              :     // check if generator production has gone wrong and is negative, reset to zero and warn
    2324         2235 :     if (electricPowerOutput < 0.0) {
    2325            0 :         if (errCountNegElectProd_ == 0) {
    2326            0 :             ShowWarningMessage(
    2327              :                 state,
    2328            0 :                 format("{} named {} is producing negative electric power, check generator inputs.", generatorTypeNames[(int)generatorType], name));
    2329            0 :             ShowContinueError(state, format("Electric power production rate ={:.4R}", electricPowerOutput));
    2330            0 :             ShowContinueError(state, "The power will be set to zero, and the simulation continues... ");
    2331              :         }
    2332            0 :         ShowRecurringWarningErrorAtEnd(state,
    2333            0 :                                        format("{} named {} is producing negative electric power ", generatorTypeNames[(int)generatorType], name),
    2334            0 :                                        errCountNegElectProd_,
    2335              :                                        electricPowerOutput,
    2336              :                                        electricPowerOutput);
    2337            0 :         electricPowerOutput = 0.0;
    2338              :     }
    2339         2235 : }
    2340              : 
    2341           10 : DCtoACInverter::DCtoACInverter(EnergyPlusData &state, std::string const &objectName)
    2342           10 :     : aCPowerOut_(0.0), aCEnergyOut_(0.0), efficiency_(0.0), dCPowerIn_(0.0), dCEnergyIn_(0.0), conversionLossPower_(0.0), conversionLossEnergy_(0.0),
    2343           10 :       conversionLossEnergyDecrement_(0.0), thermLossRate_(0.0), thermLossEnergy_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0), ancillACuseRate_(0.0),
    2344           10 :       ancillACuseEnergy_(0.0), modelType_(InverterModelType::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    2345           10 :       zoneRadFract_(0.0), nominalVoltage_(0.0), nomVoltEfficiencyARR_(6, 0.0), ratedPower_(0.0), minPower_(0.0), maxPower_(0.0), minEfficiency_(0.0),
    2346           10 :       maxEfficiency_(0.0), standbyPower_(0.0)
    2347              : {
    2348              :     // initialize
    2349           10 :     nomVoltEfficiencyARR_.resize(6, 0.0);
    2350              : 
    2351              :     static constexpr std::string_view routineName = "DCtoACInverter constructor ";
    2352              : 
    2353           10 :     auto &s_ipsc = state.dataIPShortCut;
    2354              : 
    2355           10 :     bool errorsFound = false;
    2356              :     // if/when add object class name to input object this can be simplified. for now search all possible types
    2357           10 :     bool foundInverter = false;
    2358           10 :     int testInvertIndex = 0;
    2359           10 :     int invertIDFObjectNum = 0;
    2360              : 
    2361           10 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:LookUpTable", objectName);
    2362           10 :     if (testInvertIndex > 0) {
    2363            1 :         foundInverter = true;
    2364            1 :         invertIDFObjectNum = testInvertIndex;
    2365            1 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:LookUpTable";
    2366            1 :         modelType_ = InverterModelType::CECLookUpTableModel;
    2367              :     }
    2368           10 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:FunctionOfPower", objectName);
    2369           10 :     if (testInvertIndex > 0) {
    2370            0 :         foundInverter = true;
    2371            0 :         invertIDFObjectNum = testInvertIndex;
    2372            0 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:FunctionOfPower";
    2373            0 :         modelType_ = InverterModelType::CurveFuncOfPower;
    2374              :     }
    2375           10 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:Simple", objectName);
    2376           10 :     if (testInvertIndex > 0) {
    2377            7 :         foundInverter = true;
    2378            7 :         invertIDFObjectNum = testInvertIndex;
    2379            7 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:Simple";
    2380            7 :         modelType_ = InverterModelType::SimpleConstantEff;
    2381              :     }
    2382           10 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:PVWatts", objectName);
    2383           10 :     if (testInvertIndex > 0) {
    2384            2 :         foundInverter = true;
    2385            2 :         invertIDFObjectNum = testInvertIndex;
    2386            2 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:PVWatts";
    2387            2 :         modelType_ = InverterModelType::PVWatts;
    2388              :     }
    2389              : 
    2390           10 :     if (foundInverter) {
    2391              :         int NumAlphas; // Number of elements in the alpha array
    2392              :         int NumNums;   // Number of elements in the numeric array
    2393              :         int IOStat;    // IO Status when calling get input subroutine
    2394              : 
    2395           20 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    2396           10 :                                                                  s_ipsc->cCurrentModuleObject,
    2397              :                                                                  invertIDFObjectNum,
    2398           10 :                                                                  s_ipsc->cAlphaArgs,
    2399              :                                                                  NumAlphas,
    2400           10 :                                                                  s_ipsc->rNumericArgs,
    2401              :                                                                  NumNums,
    2402              :                                                                  IOStat,
    2403           10 :                                                                  s_ipsc->lNumericFieldBlanks,
    2404           10 :                                                                  s_ipsc->lAlphaFieldBlanks,
    2405           10 :                                                                  s_ipsc->cAlphaFieldNames,
    2406           10 :                                                                  s_ipsc->cNumericFieldNames);
    2407              : 
    2408           10 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    2409           10 :         name_ = s_ipsc->cAlphaArgs(1);
    2410              :         // how to verify names are unique across objects? add to GlobalNames?
    2411              : 
    2412           10 :         if (modelType_ == InverterModelType::PVWatts) {
    2413            2 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2414            2 :             zoneNum_ = 0;
    2415            2 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2416            2 :             zoneRadFract_ = 0;
    2417            8 :         } else if (s_ipsc->lAlphaFieldBlanks(2)) {
    2418            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2419              :         } else {
    2420            8 :             if ((availSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    2421            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    2422            0 :                 errorsFound = true;
    2423              :             }
    2424              : 
    2425            8 :             if (s_ipsc->lAlphaFieldBlanks(3)) {
    2426            8 :                 heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2427            0 :             } else if ((zoneNum_ = Util::FindItemInList(s_ipsc->cAlphaArgs(3), state.dataHeatBal->Zone)) == 0) {
    2428            0 :                 heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2429            0 :                 ShowWarningItemNotFound(
    2430            0 :                     state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), "Inverter heat losses will not be added to a zone");
    2431              :                 // continue with simulation but inverter losses not sent to a zone.
    2432              :             } else {
    2433            0 :                 heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    2434              :             }
    2435            8 :             zoneRadFract_ = s_ipsc->rNumericArgs(1);
    2436              :         }
    2437              : 
    2438              :         // now the input objects differ depending on class type
    2439           10 :         switch (modelType_) {
    2440            1 :         case InverterModelType::CECLookUpTableModel: {
    2441            1 :             ratedPower_ = s_ipsc->rNumericArgs(2);
    2442            1 :             standbyPower_ = s_ipsc->rNumericArgs(3);
    2443              : 
    2444            1 :             nominalVoltage_ = s_ipsc->rNumericArgs(4);
    2445            1 :             nomVoltEfficiencyARR_[0] = s_ipsc->rNumericArgs(5);
    2446            1 :             nomVoltEfficiencyARR_[1] = s_ipsc->rNumericArgs(6);
    2447            1 :             nomVoltEfficiencyARR_[2] = s_ipsc->rNumericArgs(7);
    2448            1 :             nomVoltEfficiencyARR_[3] = s_ipsc->rNumericArgs(8);
    2449            1 :             nomVoltEfficiencyARR_[4] = s_ipsc->rNumericArgs(9);
    2450            1 :             nomVoltEfficiencyARR_[5] = s_ipsc->rNumericArgs(10);
    2451            1 :             break;
    2452              :         }
    2453            0 :         case InverterModelType::CurveFuncOfPower: {
    2454            0 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
    2455            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
    2456            0 :                 errorsFound = true;
    2457            0 :             } else if ((effCurve_ = Curve::GetCurve(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
    2458            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    2459            0 :                 errorsFound = true;
    2460              :             }
    2461              : 
    2462            0 :             ratedPower_ = s_ipsc->rNumericArgs(2);
    2463            0 :             minEfficiency_ = s_ipsc->rNumericArgs(3);
    2464            0 :             maxEfficiency_ = s_ipsc->rNumericArgs(4);
    2465            0 :             minPower_ = s_ipsc->rNumericArgs(5);
    2466            0 :             maxPower_ = s_ipsc->rNumericArgs(6);
    2467            0 :             standbyPower_ = s_ipsc->rNumericArgs(7);
    2468            0 :             break;
    2469              :         }
    2470            7 :         case InverterModelType::SimpleConstantEff: {
    2471            7 :             efficiency_ = s_ipsc->rNumericArgs(2);
    2472            7 :             break;
    2473              :         }
    2474            0 :         case InverterModelType::Invalid: {
    2475              :             // do nothing
    2476            0 :             break;
    2477              :         }
    2478            2 :         case InverterModelType::PVWatts: {
    2479            2 :             pvWattsDCtoACSizeRatio_ = s_ipsc->rNumericArgs(1);
    2480            2 :             pvWattsInverterEfficiency_ = s_ipsc->rNumericArgs(2);
    2481            2 :             break;
    2482              :         }
    2483            0 :         default:
    2484            0 :             assert(false);
    2485              :         } // end switch modelType
    2486              : 
    2487           20 :         SetupOutputVariable(state,
    2488              :                             "Inverter DC to AC Efficiency",
    2489              :                             Constant::Units::None,
    2490           10 :                             efficiency_,
    2491              :                             OutputProcessor::TimeStepType::System,
    2492              :                             OutputProcessor::StoreType::Average,
    2493           10 :                             name_);
    2494           20 :         SetupOutputVariable(state,
    2495              :                             "Inverter DC Input Electricity Rate",
    2496              :                             Constant::Units::W,
    2497           10 :                             dCPowerIn_,
    2498              :                             OutputProcessor::TimeStepType::System,
    2499              :                             OutputProcessor::StoreType::Average,
    2500           10 :                             name_);
    2501           20 :         SetupOutputVariable(state,
    2502              :                             "Inverter DC Input Electricity Energy",
    2503              :                             Constant::Units::J,
    2504           10 :                             dCEnergyIn_,
    2505              :                             OutputProcessor::TimeStepType::System,
    2506              :                             OutputProcessor::StoreType::Sum,
    2507           10 :                             name_);
    2508           20 :         SetupOutputVariable(state,
    2509              :                             "Inverter AC Output Electricity Rate",
    2510              :                             Constant::Units::W,
    2511           10 :                             aCPowerOut_,
    2512              :                             OutputProcessor::TimeStepType::System,
    2513              :                             OutputProcessor::StoreType::Average,
    2514           10 :                             name_);
    2515           20 :         SetupOutputVariable(state,
    2516              :                             "Inverter AC Output Electricity Energy",
    2517              :                             Constant::Units::J,
    2518           10 :                             aCEnergyOut_,
    2519              :                             OutputProcessor::TimeStepType::System,
    2520              :                             OutputProcessor::StoreType::Sum,
    2521           10 :                             name_);
    2522           20 :         SetupOutputVariable(state,
    2523              :                             "Inverter Conversion Loss Power",
    2524              :                             Constant::Units::W,
    2525           10 :                             conversionLossPower_,
    2526              :                             OutputProcessor::TimeStepType::System,
    2527              :                             OutputProcessor::StoreType::Average,
    2528           10 :                             name_);
    2529           20 :         SetupOutputVariable(state,
    2530              :                             "Inverter Conversion Loss Energy",
    2531              :                             Constant::Units::J,
    2532           10 :                             conversionLossEnergy_,
    2533              :                             OutputProcessor::TimeStepType::System,
    2534              :                             OutputProcessor::StoreType::Sum,
    2535           10 :                             name_);
    2536           20 :         SetupOutputVariable(state,
    2537              :                             "Inverter Conversion Loss Decrement Energy",
    2538              :                             Constant::Units::J,
    2539           10 :                             conversionLossEnergyDecrement_,
    2540              :                             OutputProcessor::TimeStepType::System,
    2541              :                             OutputProcessor::StoreType::Sum,
    2542           10 :                             name_,
    2543              :                             Constant::eResource::ElectricityProduced,
    2544              :                             OutputProcessor::Group::Plant,
    2545              :                             OutputProcessor::EndUseCat::PowerConversion);
    2546           20 :         SetupOutputVariable(state,
    2547              :                             "Inverter Thermal Loss Rate",
    2548              :                             Constant::Units::W,
    2549           10 :                             thermLossRate_,
    2550              :                             OutputProcessor::TimeStepType::System,
    2551              :                             OutputProcessor::StoreType::Average,
    2552           10 :                             name_);
    2553           20 :         SetupOutputVariable(state,
    2554              :                             "Inverter Thermal Loss Energy",
    2555              :                             Constant::Units::J,
    2556           10 :                             thermLossEnergy_,
    2557              :                             OutputProcessor::TimeStepType::System,
    2558              :                             OutputProcessor::StoreType::Sum,
    2559           10 :                             name_);
    2560           20 :         SetupOutputVariable(state,
    2561              :                             "Inverter Ancillary AC Electricity Rate",
    2562              :                             Constant::Units::W,
    2563           10 :                             ancillACuseRate_,
    2564              :                             OutputProcessor::TimeStepType::System,
    2565              :                             OutputProcessor::StoreType::Average,
    2566           10 :                             name_);
    2567           20 :         SetupOutputVariable(state,
    2568              :                             "Inverter Ancillary AC Electricity Energy",
    2569              :                             Constant::Units::J,
    2570           10 :                             ancillACuseEnergy_,
    2571              :                             OutputProcessor::TimeStepType::System,
    2572              :                             OutputProcessor::StoreType::Sum,
    2573           10 :                             name_,
    2574              :                             Constant::eResource::Electricity,
    2575              :                             OutputProcessor::Group::Plant, // called cogeneration for end use table
    2576              :                             OutputProcessor::EndUseCat::Cogeneration,
    2577              :                             "DCtoACInverter Ancillary");
    2578           10 :         if (zoneNum_ > 0) {
    2579            0 :             switch (modelType_) {
    2580            0 :             case InverterModelType::SimpleConstantEff: {
    2581            0 :                 SetupZoneInternalGain(
    2582              :                     state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterInverterSimple, &qdotConvZone_, nullptr, &qdotRadZone_);
    2583            0 :                 break;
    2584              :             }
    2585            0 :             case InverterModelType::CurveFuncOfPower: {
    2586            0 :                 SetupZoneInternalGain(state,
    2587              :                                       zoneNum_,
    2588              :                                       name_,
    2589              :                                       DataHeatBalance::IntGainType::ElectricLoadCenterInverterFunctionOfPower,
    2590              :                                       &qdotConvZone_,
    2591              :                                       nullptr,
    2592              :                                       &qdotRadZone_);
    2593            0 :                 break;
    2594              :             }
    2595            0 :             case InverterModelType::CECLookUpTableModel: {
    2596            0 :                 SetupZoneInternalGain(state,
    2597              :                                       zoneNum_,
    2598              :                                       name_,
    2599              :                                       DataHeatBalance::IntGainType::ElectricLoadCenterInverterLookUpTable,
    2600              :                                       &qdotConvZone_,
    2601              :                                       nullptr,
    2602              :                                       &qdotRadZone_);
    2603            0 :                 break;
    2604              :             }
    2605            0 :             case InverterModelType::Invalid: {
    2606              :                 // do nothing
    2607            0 :                 break;
    2608              :             }
    2609            0 :             case InverterModelType::PVWatts: {
    2610            0 :                 break;
    2611              :             }
    2612            0 :             default:
    2613            0 :                 assert(false);
    2614              :             } // end switch modelType
    2615              :         }
    2616              :     } else {
    2617            0 :         ShowSevereError(state, format("{} did not find inverter name = {}", routineName, objectName));
    2618            0 :         errorsFound = true;
    2619              :     }
    2620              : 
    2621           10 :     if (errorsFound) {
    2622            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    2623              :     }
    2624           10 : }
    2625              : 
    2626            0 : void DCtoACInverter::reinitAtBeginEnvironment()
    2627              : {
    2628            0 :     ancillACuseRate_ = 0.0;
    2629            0 :     ancillACuseEnergy_ = 0.0;
    2630            0 :     qdotConvZone_ = 0.0;
    2631            0 :     qdotRadZone_ = 0.0;
    2632            0 : }
    2633              : 
    2634            0 : void DCtoACInverter::reinitZoneGainsAtBeginEnvironment()
    2635              : {
    2636            0 :     qdotConvZone_ = 0.0;
    2637            0 :     qdotRadZone_ = 0.0;
    2638            0 : }
    2639              : 
    2640            2 : void DCtoACInverter::setPVWattsDCCapacity(EnergyPlusData &state, const Real64 dcCapacity)
    2641              : {
    2642            2 :     if (modelType_ != InverterModelType::PVWatts) {
    2643            0 :         ShowFatalError(state, "Setting the DC Capacity for the inverter only works with PVWatts Inverters.");
    2644              :     }
    2645            2 :     ratedPower_ = dcCapacity / pvWattsDCtoACSizeRatio_;
    2646            2 : }
    2647              : 
    2648            1 : Real64 DCtoACInverter::pvWattsDCCapacity()
    2649              : {
    2650            1 :     return ratedPower_ * pvWattsDCtoACSizeRatio_;
    2651              : }
    2652              : 
    2653            4 : Real64 DCtoACInverter::pvWattsInverterEfficiency()
    2654              : {
    2655            4 :     return pvWattsInverterEfficiency_;
    2656              : }
    2657              : 
    2658            4 : Real64 DCtoACInverter::pvWattsDCtoACSizeRatio()
    2659              : {
    2660            4 :     return pvWattsDCtoACSizeRatio_;
    2661              : }
    2662              : 
    2663            7 : Real64 DCtoACInverter::aCPowerOut() const
    2664              : {
    2665            7 :     return aCPowerOut_;
    2666              : }
    2667              : 
    2668            7 : DCtoACInverter::InverterModelType DCtoACInverter::modelType() const
    2669              : {
    2670            7 :     return modelType_;
    2671              : }
    2672              : 
    2673            0 : std::string const &DCtoACInverter::name() const
    2674              : {
    2675            0 :     return name_;
    2676              : }
    2677              : 
    2678            0 : Real64 DCtoACInverter::getLossRateForOutputPower(EnergyPlusData &state, Real64 const powerOutOfInverter)
    2679              : {
    2680              : 
    2681              :     // need to invert, find a dCPowerIn that produces the desired AC power out
    2682              :     // use last efficiency for initial guess
    2683            0 :     if (efficiency_ > 0.0) {
    2684            0 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2685              :     } else {
    2686            0 :         dCPowerIn_ = powerOutOfInverter;
    2687            0 :         calcEfficiency(state);
    2688            0 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2689              :     }
    2690              : 
    2691            0 :     calcEfficiency(state);
    2692              :     // one more update is close enough.
    2693            0 :     if (efficiency_ > 0.0) {
    2694            0 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2695              :     }
    2696            0 :     calcEfficiency(state);
    2697            0 :     return (1.0 - efficiency_) * dCPowerIn_;
    2698              : }
    2699              : 
    2700            4 : void DCtoACInverter::calcEfficiency(EnergyPlusData &state)
    2701              : {
    2702            4 :     switch (modelType_) {
    2703            0 :     case InverterModelType::CECLookUpTableModel: {
    2704              :         // we don't model voltage, so use nominal voltage
    2705            0 :         Real64 normalizedPower = dCPowerIn_ / ratedPower_;
    2706              : 
    2707              :         // get efficiency
    2708            0 :         if (normalizedPower <= 0.1) {
    2709              :             // extrapolate or fix at 10% value? fix it for now
    2710            0 :             efficiency_ = nomVoltEfficiencyARR_[0];
    2711            0 :         } else if ((normalizedPower > 0.1) && (normalizedPower < 0.20)) {
    2712            0 :             efficiency_ = nomVoltEfficiencyARR_[0] + ((normalizedPower - 0.1) / (0.2 - 0.1)) * (nomVoltEfficiencyARR_[1] - nomVoltEfficiencyARR_[0]);
    2713            0 :         } else if (normalizedPower == 0.2) {
    2714            0 :             efficiency_ = nomVoltEfficiencyARR_[1];
    2715            0 :         } else if ((normalizedPower > 0.2) && (normalizedPower < 0.30)) {
    2716            0 :             efficiency_ = nomVoltEfficiencyARR_[1] + ((normalizedPower - 0.2) / (0.3 - 0.2)) * (nomVoltEfficiencyARR_[2] - nomVoltEfficiencyARR_[1]);
    2717            0 :         } else if (normalizedPower == 0.3) {
    2718            0 :             efficiency_ = nomVoltEfficiencyARR_[2];
    2719            0 :         } else if ((normalizedPower > 0.3) && (normalizedPower < 0.50)) {
    2720            0 :             efficiency_ = nomVoltEfficiencyARR_[2] + ((normalizedPower - 0.3) / (0.5 - 0.3)) * (nomVoltEfficiencyARR_[3] - nomVoltEfficiencyARR_[2]);
    2721            0 :         } else if (normalizedPower == 0.5) {
    2722            0 :             efficiency_ = nomVoltEfficiencyARR_[3];
    2723            0 :         } else if ((normalizedPower > 0.5) && (normalizedPower < 0.75)) {
    2724            0 :             efficiency_ = nomVoltEfficiencyARR_[3] + ((normalizedPower - 0.5) / (0.75 - 0.5)) * (nomVoltEfficiencyARR_[4] - nomVoltEfficiencyARR_[3]);
    2725            0 :         } else if (normalizedPower == 0.75) {
    2726            0 :             efficiency_ = nomVoltEfficiencyARR_[4];
    2727            0 :         } else if ((normalizedPower > 0.75) && (normalizedPower < 1.0)) {
    2728            0 :             efficiency_ =
    2729            0 :                 nomVoltEfficiencyARR_[4] + ((normalizedPower - 0.75) / (1.0 - 0.75)) * (nomVoltEfficiencyARR_[5] - nomVoltEfficiencyARR_[4]);
    2730            0 :         } else if (normalizedPower >= 1.0) {
    2731            0 :             efficiency_ = nomVoltEfficiencyARR_[5];
    2732              :         } else {
    2733            0 :             assert(false);
    2734              :         }
    2735              : 
    2736            0 :         efficiency_ = max(efficiency_, 0.0);
    2737            0 :         efficiency_ = min(efficiency_, 1.0);
    2738            0 :     } break;
    2739              : 
    2740            0 :     case InverterModelType::CurveFuncOfPower: {
    2741            0 :         Real64 normalizedPower = dCPowerIn_ / ratedPower_;
    2742            0 :         efficiency_ = effCurve_->value(state, normalizedPower);
    2743            0 :         efficiency_ = max(efficiency_, minEfficiency_);
    2744            0 :         efficiency_ = min(efficiency_, maxEfficiency_);
    2745            0 :     } break;
    2746              : 
    2747            1 :     case InverterModelType::PVWatts: {
    2748              :         // This code is lifted from ssc cmod_pvwatts5.cpp:powerout() method.
    2749              :         // It was easier to do this calculation here because we have a many to one relationship between inverter
    2750              :         // and generator whereas theirs is one to one.
    2751            1 :         Real64 constexpr etaref = 0.9637;
    2752            1 :         Real64 constexpr A = -0.0162;
    2753            1 :         Real64 constexpr B = -0.0059;
    2754            1 :         Real64 constexpr C = 0.9858;
    2755            1 :         Real64 const pdc0 = ratedPower_ / pvWattsInverterEfficiency_;
    2756            1 :         Real64 const plr = dCPowerIn_ / pdc0;
    2757            1 :         Real64 ac = 0;
    2758              : 
    2759            1 :         if (plr > 0) {
    2760              :             // normal operation
    2761            1 :             Real64 eta = (A * plr + B / plr + C) * pvWattsInverterEfficiency_ / etaref;
    2762            1 :             ac = dCPowerIn_ * eta;
    2763            1 :             if (ac > ratedPower_) {
    2764              :                 // clipping
    2765            0 :                 ac = ratedPower_;
    2766              :             }
    2767              :             // make sure no negative AC values (no parasitic nighttime losses calculated)
    2768            1 :             if (ac < 0) {
    2769            0 :                 ac = 0;
    2770              :             }
    2771            1 :             efficiency_ = ac / dCPowerIn_;
    2772              :         } else {
    2773            0 :             efficiency_ = 1.0; // Set to a non-zero reasonable value (to avoid divide by zero error)
    2774              :         }
    2775            1 :         break;
    2776              :     }
    2777            3 :     case InverterModelType::SimpleConstantEff:
    2778              :     case InverterModelType::Invalid: {
    2779              :         // do nothing
    2780            3 :         break;
    2781              :     }
    2782            0 :     default:
    2783            0 :         assert(false);
    2784              :     } // end switch
    2785            4 : }
    2786              : 
    2787            4 : void DCtoACInverter::simulate(EnergyPlusData &state, Real64 const powerIntoInverter)
    2788              : {
    2789            4 :     dCPowerIn_ = powerIntoInverter;
    2790            4 :     dCEnergyIn_ = dCPowerIn_ * (state.dataHVACGlobal->TimeStepSysSec);
    2791              :     // check availability schedule
    2792            4 :     if (availSched_->getCurrentVal() > 0.0) {
    2793              : 
    2794              :         // now calculate Inverter based on model type
    2795            4 :         calcEfficiency(state);
    2796            4 :         aCPowerOut_ = efficiency_ * dCPowerIn_;
    2797            4 :         aCEnergyOut_ = aCPowerOut_ * (state.dataHVACGlobal->TimeStepSysSec);
    2798              : 
    2799            4 :         if (aCPowerOut_ == 0.0) {
    2800            0 :             ancillACuseEnergy_ = standbyPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    2801            0 :             ancillACuseRate_ = standbyPower_;
    2802              :         } else {
    2803            4 :             ancillACuseRate_ = 0.0;
    2804            4 :             ancillACuseEnergy_ = 0.0;
    2805              :         }
    2806              :     } else { // not available per schedule, inverter is dead.
    2807              :         //  assume thermal shunt for DC in, but no standby electricity
    2808            0 :         aCPowerOut_ = 0.0;
    2809            0 :         aCEnergyOut_ = 0.0;
    2810            0 :         ancillACuseRate_ = 0.0;
    2811            0 :         ancillACuseEnergy_ = 0.0;
    2812              :     }
    2813              :     // update report variables
    2814            4 :     conversionLossPower_ = dCPowerIn_ - aCPowerOut_;
    2815            4 :     conversionLossEnergy_ = conversionLossPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    2816            4 :     conversionLossEnergyDecrement_ = -1.0 * conversionLossEnergy_;
    2817            4 :     thermLossRate_ = dCPowerIn_ - aCPowerOut_ + ancillACuseRate_;
    2818            4 :     thermLossEnergy_ = thermLossRate_ * (state.dataHVACGlobal->TimeStepSysSec);
    2819            4 :     qdotConvZone_ = thermLossRate_ * (1.0 - zoneRadFract_);
    2820            4 :     qdotRadZone_ = thermLossRate_ * zoneRadFract_;
    2821            4 : }
    2822              : 
    2823            0 : ACtoDCConverter::ACtoDCConverter(EnergyPlusData &state, std::string const &objectName)
    2824            0 :     : efficiency_(0.0), aCPowerIn_(0.0), aCEnergyIn_(0.0), dCPowerOut_(0.0), dCEnergyOut_(0.0), conversionLossPower_(0.0), conversionLossEnergy_(0.0),
    2825            0 :       conversionLossEnergyDecrement_(0.0), thermLossRate_(0.0), thermLossEnergy_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0), ancillACuseRate_(0.0),
    2826            0 :       ancillACuseEnergy_(0.0), modelType_(ConverterModelType::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    2827            0 :       zoneRadFract_(0.0), // radiative fraction for thermal losses to zone
    2828            0 :       standbyPower_(0.0), maxPower_(0.0)
    2829              : {
    2830              : 
    2831              :     static constexpr std::string_view routineName = "ACtoDCConverter constructor ";
    2832              : 
    2833            0 :     auto &s_ipsc = state.dataIPShortCut;
    2834              : 
    2835            0 :     bool errorsFound = false;
    2836              :     // if/when add object class name to input object this can be simplified. for now search all possible types
    2837              : 
    2838            0 :     int testConvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Storage:Converter", objectName);
    2839              : 
    2840            0 :     if (testConvertIndex > 0) {
    2841              :         int NumAlphas; // Number of elements in the alpha array
    2842              :         int NumNums;   // Number of elements in the numeric array
    2843              :         int IOStat;    // IO Status when calling get input subroutine
    2844            0 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Storage:Converter";
    2845              : 
    2846            0 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    2847            0 :                                                                  s_ipsc->cCurrentModuleObject,
    2848              :                                                                  testConvertIndex,
    2849            0 :                                                                  s_ipsc->cAlphaArgs,
    2850              :                                                                  NumAlphas,
    2851            0 :                                                                  s_ipsc->rNumericArgs,
    2852              :                                                                  NumNums,
    2853              :                                                                  IOStat,
    2854            0 :                                                                  s_ipsc->lNumericFieldBlanks,
    2855            0 :                                                                  s_ipsc->lAlphaFieldBlanks,
    2856            0 :                                                                  s_ipsc->cAlphaFieldNames,
    2857            0 :                                                                  s_ipsc->cNumericFieldNames);
    2858              : 
    2859            0 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    2860              : 
    2861            0 :         name_ = s_ipsc->cAlphaArgs(1);
    2862              :         // need a new general approach for verify names are unique across objects,  next gen GlobalNames
    2863              : 
    2864            0 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    2865            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2866            0 :         } else if ((availSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    2867            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    2868            0 :             errorsFound = true;
    2869              :         }
    2870              : 
    2871            0 :         modelType_ = static_cast<ConverterModelType>(getEnumValue(converterModelTypeNamesUC, s_ipsc->cAlphaArgs(3)));
    2872            0 :         if (modelType_ == ConverterModelType::Invalid) {
    2873            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    2874            0 :             errorsFound = true;
    2875              :         }
    2876              : 
    2877            0 :         switch (modelType_) {
    2878            0 :         case ConverterModelType::SimpleConstantEff: {
    2879            0 :             efficiency_ = s_ipsc->rNumericArgs(1);
    2880            0 :         } break;
    2881              : 
    2882            0 :         case ConverterModelType::CurveFuncOfPower: {
    2883            0 :             maxPower_ = s_ipsc->rNumericArgs(2);
    2884              : 
    2885            0 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
    2886            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
    2887            0 :                 errorsFound = true;
    2888            0 :             } else if ((effCurve_ = Curve::GetCurve(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
    2889            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    2890            0 :                 errorsFound = true;
    2891              :             }
    2892            0 :         } break;
    2893              : 
    2894            0 :         case ConverterModelType::Invalid: {
    2895              :             // do nothing
    2896            0 :         } break;
    2897              : 
    2898            0 :         default:
    2899            0 :             assert(false);
    2900              :         } // end switch
    2901              : 
    2902            0 :         standbyPower_ = s_ipsc->rNumericArgs(3);
    2903              : 
    2904            0 :         if (s_ipsc->lAlphaFieldBlanks(5)) {
    2905            0 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2906            0 :         } else if ((zoneNum_ = Util::FindItemInList(s_ipsc->cAlphaArgs(5), state.dataHeatBal->Zone)) == 0) {
    2907            0 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2908            0 :             ShowWarningItemNotFound(
    2909            0 :                 state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "Inverter heat losses will not be added to a zone");
    2910              :             // continue with simulation but inverter losses not sent to a zone.
    2911              :         } else {
    2912            0 :             heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    2913              :         }
    2914              : 
    2915            0 :         zoneRadFract_ = s_ipsc->rNumericArgs(4);
    2916              : 
    2917            0 :         SetupOutputVariable(state,
    2918              :                             "Converter AC to DC Efficiency",
    2919              :                             Constant::Units::None,
    2920            0 :                             efficiency_,
    2921              :                             OutputProcessor::TimeStepType::System,
    2922              :                             OutputProcessor::StoreType::Average,
    2923            0 :                             name_);
    2924            0 :         SetupOutputVariable(state,
    2925              :                             "Converter AC Input Electricity Rate",
    2926              :                             Constant::Units::W,
    2927            0 :                             aCPowerIn_,
    2928              :                             OutputProcessor::TimeStepType::System,
    2929              :                             OutputProcessor::StoreType::Average,
    2930            0 :                             name_);
    2931            0 :         SetupOutputVariable(state,
    2932              :                             "Converter AC Input Electricity Energy",
    2933              :                             Constant::Units::J,
    2934            0 :                             aCEnergyIn_,
    2935              :                             OutputProcessor::TimeStepType::System,
    2936              :                             OutputProcessor::StoreType::Sum,
    2937            0 :                             name_);
    2938            0 :         SetupOutputVariable(state,
    2939              :                             "Converter DC Output Electricity Rate",
    2940              :                             Constant::Units::W,
    2941            0 :                             dCPowerOut_,
    2942              :                             OutputProcessor::TimeStepType::System,
    2943              :                             OutputProcessor::StoreType::Average,
    2944            0 :                             name_);
    2945            0 :         SetupOutputVariable(state,
    2946              :                             "Converter DC Output Electricity Energy",
    2947              :                             Constant::Units::J,
    2948            0 :                             dCEnergyOut_,
    2949              :                             OutputProcessor::TimeStepType::System,
    2950              :                             OutputProcessor::StoreType::Sum,
    2951            0 :                             name_);
    2952            0 :         SetupOutputVariable(state,
    2953              :                             "Converter Electricity Loss Rate",
    2954              :                             Constant::Units::W,
    2955            0 :                             conversionLossPower_,
    2956              :                             OutputProcessor::TimeStepType::System,
    2957              :                             OutputProcessor::StoreType::Average,
    2958            0 :                             name_);
    2959            0 :         SetupOutputVariable(state,
    2960              :                             "Converter Electricity Loss Energy",
    2961              :                             Constant::Units::J,
    2962            0 :                             conversionLossEnergy_,
    2963              :                             OutputProcessor::TimeStepType::System,
    2964              :                             OutputProcessor::StoreType::Sum,
    2965            0 :                             name_);
    2966            0 :         SetupOutputVariable(state,
    2967              :                             "Converter Electricity Loss Decrement Energy",
    2968              :                             Constant::Units::J,
    2969            0 :                             conversionLossEnergyDecrement_,
    2970              :                             OutputProcessor::TimeStepType::System,
    2971              :                             OutputProcessor::StoreType::Sum,
    2972            0 :                             name_,
    2973              :                             Constant::eResource::ElectricityProduced,
    2974              :                             OutputProcessor::Group::Plant,
    2975              :                             OutputProcessor::EndUseCat::PowerConversion);
    2976            0 :         SetupOutputVariable(state,
    2977              :                             "Converter Thermal Loss Rate",
    2978              :                             Constant::Units::W,
    2979            0 :                             thermLossRate_,
    2980              :                             OutputProcessor::TimeStepType::System,
    2981              :                             OutputProcessor::StoreType::Average,
    2982            0 :                             name_);
    2983            0 :         SetupOutputVariable(state,
    2984              :                             "Converter Thermal Loss Energy",
    2985              :                             Constant::Units::J,
    2986            0 :                             thermLossEnergy_,
    2987              :                             OutputProcessor::TimeStepType::System,
    2988              :                             OutputProcessor::StoreType::Sum,
    2989            0 :                             name_);
    2990            0 :         SetupOutputVariable(state,
    2991              :                             "Converter Ancillary AC Electricity Rate",
    2992              :                             Constant::Units::W,
    2993            0 :                             ancillACuseRate_,
    2994              :                             OutputProcessor::TimeStepType::System,
    2995              :                             OutputProcessor::StoreType::Average,
    2996            0 :                             name_);
    2997            0 :         SetupOutputVariable(state,
    2998              :                             "Converter Ancillary AC Electricity Energy",
    2999              :                             Constant::Units::J,
    3000            0 :                             ancillACuseEnergy_,
    3001              :                             OutputProcessor::TimeStepType::System,
    3002              :                             OutputProcessor::StoreType::Sum,
    3003            0 :                             name_,
    3004              :                             Constant::eResource::Electricity,
    3005              :                             OutputProcessor::Group::Plant, // called cogeneration for end use table
    3006              :                             OutputProcessor::EndUseCat::Cogeneration,
    3007              :                             "ACtoDCConverter Ancillary");
    3008            0 :         if (zoneNum_ > 0) {
    3009            0 :             SetupZoneInternalGain(
    3010              :                 state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterConverter, &qdotConvZone_, nullptr, &qdotRadZone_);
    3011              :         }
    3012              :     } else {
    3013            0 :         ShowSevereError(state, format("{} did not find power converter name = {}", routineName, objectName));
    3014            0 :         errorsFound = true;
    3015              :     }
    3016              : 
    3017            0 :     if (errorsFound) {
    3018            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    3019              :     }
    3020            0 : }
    3021              : 
    3022            0 : void ACtoDCConverter::reinitAtBeginEnvironment()
    3023              : {
    3024            0 :     ancillACuseRate_ = 0.0;
    3025            0 :     ancillACuseEnergy_ = 0.0;
    3026            0 :     qdotConvZone_ = 0.0;
    3027            0 :     qdotRadZone_ = 0.0;
    3028            0 : }
    3029              : 
    3030            0 : void ACtoDCConverter::reinitZoneGainsAtBeginEnvironment()
    3031              : {
    3032            0 :     qdotConvZone_ = 0.0;
    3033            0 :     qdotRadZone_ = 0.0;
    3034            0 : }
    3035              : 
    3036            0 : Real64 ACtoDCConverter::aCPowerIn() const
    3037              : {
    3038            0 :     return aCPowerIn_;
    3039              : }
    3040              : 
    3041            0 : Real64 ACtoDCConverter::getLossRateForInputPower(EnergyPlusData &state, Real64 const powerIntoConverter)
    3042              : {
    3043            0 :     aCPowerIn_ = powerIntoConverter;
    3044            0 :     calcEfficiency(state);
    3045            0 :     return (1.0 - efficiency_) * aCPowerIn_;
    3046              : }
    3047              : 
    3048            0 : std::string const &ACtoDCConverter::name() const
    3049              : {
    3050            0 :     return name_;
    3051              : }
    3052              : 
    3053            0 : void ACtoDCConverter::calcEfficiency(EnergyPlusData &state)
    3054              : {
    3055            0 :     switch (modelType_) {
    3056            0 :     case ConverterModelType::Invalid:
    3057              :     case ConverterModelType::SimpleConstantEff: {
    3058            0 :         break;
    3059              :     }
    3060            0 :     case ConverterModelType::CurveFuncOfPower: {
    3061            0 :         Real64 normalizedPower = aCPowerIn_ / maxPower_;
    3062            0 :         efficiency_ = effCurve_->value(state, normalizedPower);
    3063            0 :         break;
    3064              :     }
    3065            0 :     default:
    3066            0 :         assert(false);
    3067              :     } // end switch
    3068            0 : }
    3069              : 
    3070            0 : void ACtoDCConverter::simulate(EnergyPlusData &state, Real64 const powerOutFromConverter)
    3071              : {
    3072              :     // need to invert, find an aCPowerIn that produces the desired DC power out
    3073              : 
    3074              :     // use last efficiency for initial guess
    3075            0 :     if (availSched_->getCurrentVal() > 0.0) {
    3076              : 
    3077            0 :         aCPowerIn_ = powerOutFromConverter / efficiency_;
    3078            0 :         calcEfficiency(state), aCPowerIn_ = powerOutFromConverter / efficiency_;
    3079            0 :         calcEfficiency(state),
    3080              : 
    3081            0 :             dCPowerOut_ = aCPowerIn_ * efficiency_;
    3082              : 
    3083            0 :         if (dCPowerOut_ == 0.0) {
    3084            0 :             ancillACuseEnergy_ = standbyPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    3085            0 :             ancillACuseRate_ = standbyPower_;
    3086              :         } else {
    3087            0 :             ancillACuseRate_ = 0.0;
    3088            0 :             ancillACuseEnergy_ = 0.0;
    3089              :         }
    3090              : 
    3091              :     } else { // not available
    3092            0 :         aCPowerIn_ = 0.0;
    3093            0 :         dCPowerOut_ = 0.0;
    3094            0 :         ancillACuseRate_ = 0.0;
    3095            0 :         ancillACuseEnergy_ = 0.0;
    3096              :     }
    3097              : 
    3098              :     // update and report
    3099            0 :     aCEnergyIn_ = aCPowerIn_ * (state.dataHVACGlobal->TimeStepSysSec);
    3100            0 :     dCEnergyOut_ = dCPowerOut_ * (state.dataHVACGlobal->TimeStepSysSec);
    3101            0 :     conversionLossPower_ = aCPowerIn_ - dCPowerOut_;
    3102            0 :     conversionLossEnergy_ = conversionLossPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    3103            0 :     conversionLossEnergyDecrement_ = -1.0 * conversionLossEnergy_;
    3104            0 :     thermLossRate_ = aCPowerIn_ - dCPowerOut_ + ancillACuseRate_;
    3105            0 :     thermLossEnergy_ = thermLossRate_ * (state.dataHVACGlobal->TimeStepSysSec);
    3106            0 :     qdotConvZone_ = thermLossRate_ * (1.0 - zoneRadFract_);
    3107            0 :     qdotRadZone_ = thermLossRate_ * zoneRadFract_;
    3108            0 : }
    3109              : 
    3110            9 : ElectricStorage::ElectricStorage( // main constructor
    3111              :     EnergyPlusData &state,
    3112            9 :     std::string const &objectName)
    3113            9 :     : storedPower_(0.0), storedEnergy_(0.0), drawnPower_(0.0), drawnEnergy_(0.0), decrementedEnergyStored_(0.0), maxRainflowArrayBounds_(100),
    3114            9 :       myWarmUpFlag_(false), storageModelMode_(StorageModelType::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    3115            9 :       zoneRadFract_(0.0), startingEnergyStored_(0.0), energeticEfficCharge_(0.0), energeticEfficDischarge_(0.0), maxPowerDraw_(0.0),
    3116            9 :       maxPowerStore_(0.0), maxEnergyCapacity_(0.0), parallelNum_(0), seriesNum_(0), numBattery_(0), cycleBinNum_(0), startingSOC_(0.0),
    3117            9 :       maxAhCapacity_(0.0), availableFrac_(0.0), chargeConversionRate_(0.0), chargedOCV_(0.0), dischargedOCV_(0.0), internalR_(0.0),
    3118            9 :       maxDischargeI_(0.0), cutoffV_(0.0), maxChargeRate_(0.0), lifeCalculation_(false), liIon_dcToDcChargingEff_(0.0), liIon_mass_(0.0),
    3119            9 :       liIon_surfaceArea_(0.0), liIon_Cp_(0.0), liIon_heatTransferCoef_(0.0), liIon_Vfull_(0.0), liIon_Vexp_(0.0), liIon_Vnom_(0.0),
    3120            9 :       liIon_Vnom_default_(0.0), liIon_Qfull_(0.0), liIon_Qexp_(0.0), liIon_Qnom_(0.0), liIon_C_rate_(0.0), thisTimeStepStateOfCharge_(0.0),
    3121            9 :       lastTimeStepStateOfCharge_(0.0), pelNeedFromStorage_(0.0), pelFromStorage_(0.0), pelIntoStorage_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0),
    3122            9 :       timeElapsed_(0.0), thisTimeStepAvailable_(0.0), thisTimeStepBound_(0.0), lastTimeStepAvailable_(0.0), lastTimeStepBound_(0.0),
    3123           27 :       lastTwoTimeStepAvailable_(0.0), lastTwoTimeStepBound_(0.0), count0_(0), electEnergyinStorage_(0.0), thermLossRate_(0.0), thermLossEnergy_(0.0),
    3124            9 :       storageMode_(0), absoluteSOC_(0.0), fractionSOC_(0.0), batteryCurrent_(0.0), batteryVoltage_(0.0), batteryDamage_(0.0), batteryTemperature_(0.0)
    3125              : {
    3126              : 
    3127              :     static constexpr std::string_view routineName = "ElectricStorage constructor ";
    3128              : 
    3129            9 :     auto &s_ipsc = state.dataIPShortCut;
    3130              : 
    3131            9 :     bool errorsFound = false;
    3132              :     // if/when add object class name to input object this can be simplified. for now search all possible types
    3133            9 :     bool foundStorage = false;
    3134            9 :     int storageIDFObjectNum = 0;
    3135              : 
    3136              :     const std::array<std::pair<std::string, StorageModelType>, 3> storageTypes{
    3137            0 :         {{"ElectricLoadCenter:Storage:Simple", StorageModelType::SimpleBucketStorage},
    3138            0 :          {"ElectricLoadCenter:Storage:Battery", StorageModelType::KIBaMBattery},
    3139            9 :          {"ElectricLoadCenter:Storage:LiIonNMCBattery", StorageModelType::LiIonNmcBattery}}};
    3140              : 
    3141           16 :     for (auto &item : storageTypes) {
    3142           16 :         int testStorageIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, item.first, objectName);
    3143           16 :         if (testStorageIndex > 0) {
    3144            9 :             foundStorage = true;
    3145            9 :             storageIDFObjectNum = testStorageIndex;
    3146            9 :             s_ipsc->cCurrentModuleObject = item.first;
    3147            9 :             storageModelMode_ = item.second;
    3148            9 :             break;
    3149              :         }
    3150              :     }
    3151              : 
    3152            9 :     if (foundStorage) {
    3153              :         int numAlphas; // Number of elements in the alpha array
    3154              :         int numNums;   // Number of elements in the numeric array
    3155              :         int iOStat;    // IO Status when calling get input subroutine
    3156           18 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3157            9 :                                                                  s_ipsc->cCurrentModuleObject,
    3158              :                                                                  storageIDFObjectNum,
    3159            9 :                                                                  s_ipsc->cAlphaArgs,
    3160              :                                                                  numAlphas,
    3161            9 :                                                                  s_ipsc->rNumericArgs,
    3162              :                                                                  numNums,
    3163              :                                                                  iOStat,
    3164            9 :                                                                  s_ipsc->lNumericFieldBlanks,
    3165            9 :                                                                  s_ipsc->lAlphaFieldBlanks,
    3166            9 :                                                                  s_ipsc->cAlphaFieldNames,
    3167            9 :                                                                  s_ipsc->cNumericFieldNames);
    3168              : 
    3169            9 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    3170              : 
    3171            9 :         name_ = s_ipsc->cAlphaArgs(1);
    3172              :         // how to verify names are unique across objects? add to GlobalNames?
    3173              : 
    3174            9 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    3175            3 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    3176            6 :         } else if ((availSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    3177            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    3178            0 :             errorsFound = true;
    3179              :         }
    3180              : 
    3181            9 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    3182            9 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    3183            0 :         } else if ((zoneNum_ = Util::FindItemInList(s_ipsc->cAlphaArgs(3), state.dataHeatBal->Zone)) == 0) {
    3184            0 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    3185            0 :             ShowWarningItemNotFound(
    3186            0 :                 state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), "Storage heat losses will not be added to a zone");
    3187              :             // continue with simulation but storage losses not sent to a zone.
    3188              :         } else {
    3189            0 :             heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    3190              :         }
    3191              : 
    3192            9 :         zoneRadFract_ = s_ipsc->rNumericArgs(1);
    3193              : 
    3194            9 :         switch (storageModelMode_) {
    3195              : 
    3196            5 :         case StorageModelType::SimpleBucketStorage: {
    3197            5 :             energeticEfficCharge_ = checkUserEfficiencyInput(state, s_ipsc->rNumericArgs(2), true, name_, errorsFound);
    3198            5 :             energeticEfficDischarge_ = checkUserEfficiencyInput(state, s_ipsc->rNumericArgs(3), false, name_, errorsFound);
    3199            5 :             maxEnergyCapacity_ = s_ipsc->rNumericArgs(4);
    3200            5 :             maxPowerDraw_ = s_ipsc->rNumericArgs(5);
    3201            5 :             maxPowerStore_ = s_ipsc->rNumericArgs(6);
    3202            5 :             startingEnergyStored_ = s_ipsc->rNumericArgs(7);
    3203           10 :             SetupOutputVariable(state,
    3204              :                                 "Electric Storage Simple Charge State",
    3205              :                                 Constant::Units::J,
    3206            5 :                                 electEnergyinStorage_,
    3207              :                                 OutputProcessor::TimeStepType::System,
    3208              :                                 OutputProcessor::StoreType::Average,
    3209            5 :                                 name_); // issue #4921
    3210            5 :             break;
    3211              :         }
    3212              : 
    3213            1 :         case StorageModelType::KIBaMBattery: {
    3214            1 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
    3215            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
    3216            0 :                 errorsFound = true;
    3217            1 :             } else if ((chargeCurve_ = Curve::GetCurve(state, s_ipsc->cAlphaArgs(4))) == nullptr) { // voltage calculation for charging
    3218            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    3219            0 :                 errorsFound = true;
    3220            1 :             } else if (chargeCurve_->numDims != 1) {
    3221            0 :                 Curve::ShowSevereCurveDims(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4), "1", chargeCurve_->numDims);
    3222            0 :                 errorsFound = true;
    3223              :             }
    3224              : 
    3225            1 :             if (s_ipsc->lAlphaFieldBlanks(5)) {
    3226            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
    3227            0 :                 errorsFound = true;
    3228            1 :             } else if ((dischargeCurve_ = Curve::GetCurve(state, s_ipsc->cAlphaArgs(5))) == nullptr) { // voltage calculation for discharging
    3229            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
    3230            0 :                 errorsFound = true;
    3231            1 :             } else if (dischargeCurve_->numDims != 1) {
    3232            0 :                 Curve::ShowSevereCurveDims(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "1", dischargeCurve_->numDims);
    3233            0 :                 errorsFound = true;
    3234              :             }
    3235              : 
    3236            1 :             if (s_ipsc->lAlphaFieldBlanks(6)) {
    3237            0 :                 lifeCalculation_ = false;
    3238            1 :             } else if (BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(6)); bs != BooleanSwitch::Invalid) {
    3239            1 :                 lifeCalculation_ = static_cast<bool>(bs);
    3240              :             } else {
    3241            0 :                 ShowWarningInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6), "No");
    3242            0 :                 lifeCalculation_ = false;
    3243              :             }
    3244              : 
    3245            1 :             if (lifeCalculation_) {
    3246            1 :                 if (s_ipsc->lAlphaFieldBlanks(7)) {
    3247            0 :                     ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
    3248            0 :                     errorsFound = true;
    3249            1 :                 } else if ((lifeCurve_ = Curve::GetCurve(state, s_ipsc->cAlphaArgs(7))) == nullptr) { // Battery life calculation
    3250            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
    3251            0 :                     errorsFound = true;
    3252            1 :                 } else if (lifeCurve_->numDims != 1) {
    3253            0 :                     Curve::ShowSevereCurveDims(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7), "1", lifeCurve_->numDims);
    3254            0 :                     errorsFound = true;
    3255              :                 }
    3256              : 
    3257            1 :                 cycleBinNum_ = s_ipsc->rNumericArgs(14);
    3258              : 
    3259            1 :                 if (!errorsFound) { // life cycle calculation for this battery, allocate arrays for degradation calculation
    3260              :                                     // std::vector is zero base instead of 1, so first index is now 0.
    3261            1 :                     b10_.resize(maxRainflowArrayBounds_ + 1, 0.0);
    3262            1 :                     x0_.resize(maxRainflowArrayBounds_ + 1, 0);
    3263            1 :                     nmb0_.resize(cycleBinNum_, 0.0);
    3264            1 :                     oneNmb0_.resize(cycleBinNum_, 0.0);
    3265              :                 }
    3266              :             }
    3267              : 
    3268            1 :             parallelNum_ = s_ipsc->rNumericArgs(2);
    3269            1 :             seriesNum_ = state.dataIPShortCut->rNumericArgs(3);
    3270            1 :             numBattery_ = parallelNum_ * seriesNum_;
    3271            1 :             maxAhCapacity_ = state.dataIPShortCut->rNumericArgs(4);
    3272            1 :             startingSOC_ = state.dataIPShortCut->rNumericArgs(5);
    3273            1 :             availableFrac_ = state.dataIPShortCut->rNumericArgs(6);
    3274            1 :             chargeConversionRate_ = state.dataIPShortCut->rNumericArgs(7);
    3275            1 :             chargedOCV_ = state.dataIPShortCut->rNumericArgs(8);
    3276            1 :             dischargedOCV_ = state.dataIPShortCut->rNumericArgs(9);
    3277            1 :             internalR_ = state.dataIPShortCut->rNumericArgs(10);
    3278            1 :             maxDischargeI_ = state.dataIPShortCut->rNumericArgs(11);
    3279            1 :             cutoffV_ = state.dataIPShortCut->rNumericArgs(12);
    3280            1 :             maxChargeRate_ = state.dataIPShortCut->rNumericArgs(13);
    3281              : 
    3282              :             // Check charging and discharging curves to make sure charging curve always gives a higher voltage (#8817)
    3283            1 :             if (!errorsFound && chargeCurve_ != nullptr && dischargeCurve_ != nullptr) {
    3284            1 :                 checkChargeDischargeVoltageCurves(state, name_, chargedOCV_, dischargedOCV_, chargeCurve_, dischargeCurve_);
    3285              :             }
    3286              : 
    3287            1 :             break;
    3288              :         }
    3289            3 :         case StorageModelType::LiIonNmcBattery: {
    3290            3 :             lifeCalculation_ = (Util::SameString(state.dataIPShortCut->cAlphaArgs(4), "KandlerSmith") || state.dataIPShortCut->lAlphaFieldBlanks(4));
    3291            3 :             seriesNum_ = static_cast<int>(state.dataIPShortCut->rNumericArgs(2));
    3292            3 :             parallelNum_ = static_cast<int>(state.dataIPShortCut->rNumericArgs(3));
    3293            3 :             startingSOC_ = state.dataIPShortCut->lNumericFieldBlanks(4) ? 0.5 : state.dataIPShortCut->rNumericArgs(4);
    3294            3 :             liIon_dcToDcChargingEff_ = state.dataIPShortCut->lNumericFieldBlanks(5) ? 0.95 : state.dataIPShortCut->rNumericArgs(5);
    3295            3 :             liIon_mass_ = state.dataIPShortCut->rNumericArgs(6);
    3296            3 :             liIon_surfaceArea_ = state.dataIPShortCut->rNumericArgs(7);
    3297            3 :             liIon_Cp_ = state.dataIPShortCut->lNumericFieldBlanks(8) ? 1500.0 : state.dataIPShortCut->rNumericArgs(8);
    3298            3 :             liIon_heatTransferCoef_ = state.dataIPShortCut->lNumericFieldBlanks(9) ? 7.5 : state.dataIPShortCut->rNumericArgs(9);
    3299            3 :             liIon_Vfull_ = state.dataIPShortCut->lNumericFieldBlanks(10) ? 4.2 : state.dataIPShortCut->rNumericArgs(10);
    3300            3 :             liIon_Vexp_ = state.dataIPShortCut->lNumericFieldBlanks(11) ? 3.53 : state.dataIPShortCut->rNumericArgs(11);
    3301            3 :             liIon_Vnom_ = state.dataIPShortCut->lNumericFieldBlanks(12) ? 3.342 : state.dataIPShortCut->rNumericArgs(12);
    3302            3 :             if (liIon_Vfull_ < liIon_Vexp_ || liIon_Vexp_ < liIon_Vnom_) {
    3303            2 :                 ShowSevereError(
    3304              :                     state,
    3305            3 :                     format(
    3306            2 :                         "{}{}=\"{}\", invalid entry.", routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
    3307            2 :                 ShowContinueError(state,
    3308            3 :                                   format("{} must be greater than {},",
    3309            1 :                                          state.dataIPShortCut->cNumericFieldNames(10),
    3310            1 :                                          state.dataIPShortCut->cNumericFieldNames(11)));
    3311            1 :                 ShowContinueError(state, format("which must be greater than {}.", state.dataIPShortCut->cNumericFieldNames(12)));
    3312            4 :                 for (int i = 10; i <= 12; ++i) {
    3313            6 :                     ShowContinueError(state,
    3314            6 :                                       format("{} = {:.3R}", state.dataIPShortCut->cNumericFieldNames(i), state.dataIPShortCut->rNumericArgs(i)));
    3315              :                 }
    3316            1 :                 errorsFound = true;
    3317              :             }
    3318            3 :             liIon_Vnom_default_ = state.dataIPShortCut->lNumericFieldBlanks(13) ? 3.342 : state.dataIPShortCut->rNumericArgs(13);
    3319            3 :             liIon_Qfull_ = state.dataIPShortCut->lNumericFieldBlanks(14) ? 3.2 : state.dataIPShortCut->rNumericArgs(14);
    3320            3 :             liIon_Qexp_ =
    3321            3 :                 state.dataIPShortCut->lNumericFieldBlanks(15) ? 0.8075 * liIon_Qfull_ : state.dataIPShortCut->rNumericArgs(15) * liIon_Qfull_;
    3322            3 :             liIon_Qnom_ =
    3323            3 :                 state.dataIPShortCut->lNumericFieldBlanks(16) ? 0.976875 * liIon_Qfull_ : state.dataIPShortCut->rNumericArgs(16) * liIon_Qfull_;
    3324            3 :             if (liIon_Qexp_ >= liIon_Qnom_) {
    3325            2 :                 ShowSevereError(
    3326              :                     state,
    3327            3 :                     format(
    3328            2 :                         "{}{}=\"{}\", invalid entry.", routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
    3329            2 :                 ShowContinueError(state,
    3330            3 :                                   format("{} must be greater than {}.",
    3331            1 :                                          state.dataIPShortCut->cNumericFieldNames(16),
    3332            1 :                                          state.dataIPShortCut->cNumericFieldNames(15)));
    3333            3 :                 for (int i = 15; i <= 16; ++i) {
    3334            4 :                     ShowContinueError(state,
    3335            4 :                                       format("{} = {:.3R}", state.dataIPShortCut->cNumericFieldNames(i), state.dataIPShortCut->rNumericArgs(i)));
    3336              :                 }
    3337            1 :                 errorsFound = true;
    3338              :             }
    3339            3 :             liIon_C_rate_ = state.dataIPShortCut->lNumericFieldBlanks(17) ? 1.0 : state.dataIPShortCut->rNumericArgs(17);
    3340            3 :             internalR_ = state.dataIPShortCut->lNumericFieldBlanks(18) ? 0.09 : state.dataIPShortCut->rNumericArgs(18);
    3341              : 
    3342            3 :             maxAhCapacity_ = liIon_Qfull_ * parallelNum_;
    3343              : 
    3344            3 :             if (!errorsFound) {
    3345              :                 // Set the Lifetime model in SSC
    3346              :                 // I'm using a raw pointer here because the the battery_t constructor expects it.
    3347              :                 // The pointer is then passed into the battery_t where it is converted into a unique_ptr and persists along with that object.
    3348              :                 // Therefore I am not deleting this pointer here because that will be handled by the battery_t class.
    3349              :                 lifetime_t *battLifetime;
    3350            2 :                 if (lifeCalculation_) {
    3351            2 :                     battLifetime = new lifetime_nmc_t(state.dataHVACGlobal->TimeStepSys);
    3352              :                 } else {
    3353              :                     // This sets a lifetime model where the capacity is always 100%.
    3354            0 :                     std::vector<double> tblVals{{20, 0, 100, 20, 5000, 100, 20, 10000, 100, 80, 0, 100, 80, 1000, 100, 80, 2000, 100}};
    3355            0 :                     util::matrix_t<double> battLifetimeMatrix(6, 3, &tblVals);
    3356            0 :                     battLifetime = new lifetime_calendar_cycle_t(battLifetimeMatrix, state.dataHVACGlobal->TimeStepSys);
    3357            0 :                 }
    3358              : 
    3359              :                 // Create the SSC battery object
    3360            4 :                 ssc_battery_ = std::unique_ptr<battery_t>(
    3361            2 :                     new battery_t(state.dataHVACGlobal->TimeStepSys,
    3362              :                                   battery_params::CHEM::LITHIUM_ION,
    3363              :                                   new capacity_lithium_ion_t(maxAhCapacity_, // Capacity of the whole battery
    3364            2 :                                                              startingSOC_ * 100.0,
    3365              :                                                              100.0, // Reset later
    3366              :                                                              0.0,   // Reset later
    3367            2 :                                                              state.dataHVACGlobal->TimeStepSys),
    3368              :                                   new voltage_dynamic_t(seriesNum_,
    3369              :                                                         parallelNum_,
    3370              :                                                         liIon_Vnom_default_,
    3371              :                                                         liIon_Vfull_,
    3372              :                                                         liIon_Vexp_,
    3373              :                                                         liIon_Vnom_,
    3374              :                                                         liIon_Qfull_, // Capacity of one cell
    3375              :                                                         liIon_Qexp_,
    3376              :                                                         liIon_Qnom_,
    3377              :                                                         liIon_C_rate_,
    3378              :                                                         internalR_,
    3379            2 :                                                         state.dataHVACGlobal->TimeStepSys),
    3380              :                                   battLifetime,
    3381            2 :                                   new thermal_t(state.dataHVACGlobal->TimeStepSys,
    3382              :                                                 liIon_mass_,
    3383              :                                                 liIon_surfaceArea_,
    3384            2 :                                                 internalR_ * seriesNum_ / parallelNum_, // Electric resistance of the whole battery
    3385              :                                                 liIon_Cp_,
    3386              :                                                 liIon_heatTransferCoef_,
    3387              :                                                 20.0 // Picking a temperature for now, will reset before each run.
    3388            2 :                                                 ),
    3389           10 :                                   nullptr));
    3390            2 :                 ssc_lastBatteryState_ = std::make_unique<battery_state>(ssc_battery_->get_state());
    3391            2 :                 ssc_lastBatteryTimeStep_ = ssc_battery_->get_params().dt_hr;
    3392            2 :                 ssc_initBatteryState_ = std::make_unique<battery_state>(ssc_battery_->get_state());
    3393              :             }
    3394              : 
    3395            3 :             break;
    3396              :         }
    3397            0 :         case StorageModelType::Invalid: {
    3398              :             // do nothing
    3399            0 :             break;
    3400              :         }
    3401            0 :         default:
    3402            0 :             assert(false);
    3403              :         } // switch storage model type
    3404              : 
    3405            9 :         if (storageModelMode_ == StorageModelType::KIBaMBattery || storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3406            4 :             SetupOutputVariable(state,
    3407              :                                 "Electric Storage Operating Mode Index",
    3408              :                                 Constant::Units::None,
    3409            4 :                                 storageMode_,
    3410              :                                 OutputProcessor::TimeStepType::System,
    3411              :                                 OutputProcessor::StoreType::Average,
    3412            4 :                                 name_);
    3413            8 :             SetupOutputVariable(state,
    3414              :                                 "Electric Storage Battery Charge State",
    3415              :                                 Constant::Units::Ah,
    3416            4 :                                 absoluteSOC_,
    3417              :                                 OutputProcessor::TimeStepType::System,
    3418              :                                 OutputProcessor::StoreType::Average,
    3419            4 :                                 name_); // issue #4921
    3420            8 :             SetupOutputVariable(state,
    3421              :                                 "Electric Storage Charge Fraction",
    3422              :                                 Constant::Units::None,
    3423            4 :                                 fractionSOC_,
    3424              :                                 OutputProcessor::TimeStepType::System,
    3425              :                                 OutputProcessor::StoreType::Average,
    3426            4 :                                 name_);
    3427            8 :             SetupOutputVariable(state,
    3428              :                                 "Electric Storage Total Current",
    3429              :                                 Constant::Units::A,
    3430            4 :                                 batteryCurrent_,
    3431              :                                 OutputProcessor::TimeStepType::System,
    3432              :                                 OutputProcessor::StoreType::Average,
    3433            4 :                                 name_);
    3434            8 :             SetupOutputVariable(state,
    3435              :                                 "Electric Storage Total Voltage",
    3436              :                                 Constant::Units::V,
    3437            4 :                                 batteryVoltage_,
    3438              :                                 OutputProcessor::TimeStepType::System,
    3439              :                                 OutputProcessor::StoreType::Average,
    3440            4 :                                 name_);
    3441              : 
    3442            4 :             if (lifeCalculation_) {
    3443            6 :                 SetupOutputVariable(state,
    3444              :                                     "Electric Storage Degradation Fraction",
    3445              :                                     Constant::Units::None,
    3446            3 :                                     batteryDamage_,
    3447              :                                     OutputProcessor::TimeStepType::System,
    3448              :                                     OutputProcessor::StoreType::Average,
    3449            3 :                                     name_);
    3450              :             }
    3451              :         }
    3452              : 
    3453           18 :         SetupOutputVariable(state,
    3454              :                             "Electric Storage Charge Power",
    3455              :                             Constant::Units::W,
    3456            9 :                             storedPower_,
    3457              :                             OutputProcessor::TimeStepType::System,
    3458              :                             OutputProcessor::StoreType::Average,
    3459            9 :                             name_);
    3460           18 :         SetupOutputVariable(state,
    3461              :                             "Electric Storage Charge Energy",
    3462              :                             Constant::Units::J,
    3463            9 :                             storedEnergy_,
    3464              :                             OutputProcessor::TimeStepType::System,
    3465              :                             OutputProcessor::StoreType::Sum,
    3466            9 :                             name_);
    3467           18 :         SetupOutputVariable(state,
    3468              :                             "Electric Storage Production Decrement Energy",
    3469              :                             Constant::Units::J,
    3470            9 :                             decrementedEnergyStored_,
    3471              :                             OutputProcessor::TimeStepType::System,
    3472              :                             OutputProcessor::StoreType::Sum,
    3473            9 :                             name_,
    3474              :                             Constant::eResource::ElectricityProduced,
    3475              :                             OutputProcessor::Group::Plant,
    3476              :                             OutputProcessor::EndUseCat::ElectricStorage);
    3477           18 :         SetupOutputVariable(state,
    3478              :                             "Electric Storage Discharge Power",
    3479              :                             Constant::Units::W,
    3480            9 :                             drawnPower_,
    3481              :                             OutputProcessor::TimeStepType::System,
    3482              :                             OutputProcessor::StoreType::Average,
    3483            9 :                             name_);
    3484           18 :         SetupOutputVariable(state,
    3485              :                             "Electric Storage Discharge Energy",
    3486              :                             Constant::Units::J,
    3487            9 :                             drawnEnergy_,
    3488              :                             OutputProcessor::TimeStepType::System,
    3489              :                             OutputProcessor::StoreType::Sum,
    3490            9 :                             name_,
    3491              :                             Constant::eResource::ElectricityProduced,
    3492              :                             OutputProcessor::Group::Plant,
    3493              :                             OutputProcessor::EndUseCat::ElectricStorage);
    3494           18 :         SetupOutputVariable(state,
    3495              :                             "Electric Storage Thermal Loss Rate",
    3496              :                             Constant::Units::W,
    3497            9 :                             thermLossRate_,
    3498              :                             OutputProcessor::TimeStepType::System,
    3499              :                             OutputProcessor::StoreType::Average,
    3500            9 :                             name_);
    3501           18 :         SetupOutputVariable(state,
    3502              :                             "Electric Storage Thermal Loss Energy",
    3503              :                             Constant::Units::J,
    3504            9 :                             thermLossEnergy_,
    3505              :                             OutputProcessor::TimeStepType::System,
    3506              :                             OutputProcessor::StoreType::Sum,
    3507            9 :                             name_);
    3508            9 :         if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
    3509            0 :             if (storageModelMode_ == StorageModelType::SimpleBucketStorage) {
    3510            0 :                 SetupEMSInternalVariable(state, "Electrical Storage Simple Maximum Capacity", name_, "[J]", maxEnergyCapacity_);
    3511            0 :             } else if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3512            0 :                 SetupEMSInternalVariable(state, "Electrical Storage Battery Maximum Capacity", name_, "[Ah]", maxAhCapacity_);
    3513              :             }
    3514              :         }
    3515            9 :         if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3516            6 :             SetupOutputVariable(state,
    3517              :                                 "Electric Storage Battery Temperature",
    3518              :                                 Constant::Units::C,
    3519            3 :                                 batteryTemperature_,
    3520              :                                 OutputProcessor::TimeStepType::System,
    3521              :                                 OutputProcessor::StoreType::Average,
    3522            3 :                                 name_);
    3523              :         }
    3524              : 
    3525            9 :         if (zoneNum_ > 0) {
    3526            0 :             switch (storageModelMode_) {
    3527            0 :             case StorageModelType::SimpleBucketStorage: {
    3528            0 :                 SetupZoneInternalGain(
    3529              :                     state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterStorageSimple, &qdotConvZone_, nullptr, &qdotRadZone_);
    3530            0 :                 break;
    3531              :             }
    3532            0 :             case StorageModelType::KIBaMBattery: {
    3533            0 :                 SetupZoneInternalGain(
    3534              :                     state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterStorageBattery, &qdotConvZone_, nullptr, &qdotRadZone_);
    3535            0 :                 break;
    3536              :             }
    3537            0 :             case StorageModelType::LiIonNmcBattery: {
    3538            0 :                 SetupZoneInternalGain(state,
    3539              :                                       zoneNum_,
    3540              :                                       name_,
    3541              :                                       DataHeatBalance::IntGainType::ElectricLoadCenterStorageLiIonNmcBattery,
    3542              :                                       &qdotConvZone_,
    3543              :                                       nullptr,
    3544              :                                       &qdotRadZone_);
    3545            0 :                 break;
    3546              :             }
    3547            0 :             case StorageModelType::Invalid: {
    3548              :                 // do nothing
    3549            0 :                 break;
    3550              :             }
    3551            0 :             default:
    3552            0 :                 assert(false);
    3553              :             } // switch storage model type
    3554              :         }
    3555              :     } else { // storage not found
    3556            0 :         ShowSevereError(state, format("{} did not find storage name = {}", routineName, objectName));
    3557            0 :         errorsFound = true;
    3558              :     }
    3559            9 :     if (errorsFound) {
    3560            2 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    3561              :     }
    3562           17 : }
    3563              : 
    3564           14 : Real64 checkUserEfficiencyInput(EnergyPlusData &state, Real64 userInputValue, bool isCharging, std::string const &deviceName, bool &errorsFound)
    3565              : {
    3566           14 :     Real64 constexpr minChargeEfficiency = 0.001;
    3567           14 :     Real64 constexpr minDischargeEfficiency = 0.001;
    3568              : 
    3569              :     // Fix for Defect #8867.  Do not allow either efficiency to be zero as it will lead to a divide by zero (NaN).
    3570           14 :     if (isCharging) {
    3571            7 :         if (userInputValue < minChargeEfficiency) {
    3572            2 :             ShowSevereError(state,
    3573            2 :                             format("ElectricStorage charge efficiency was too low.  This occurred for electric storage unit named {}", deviceName));
    3574            2 :             ShowContinueError(state, "Please check your input value  for this electric storage unit and fix the charge efficiency.");
    3575            1 :             errorsFound = true;
    3576            1 :             return minChargeEfficiency;
    3577              :         } else {
    3578            6 :             return userInputValue;
    3579              :         }
    3580              :     } else { // discharging
    3581            7 :         if (userInputValue < minDischargeEfficiency) {
    3582            2 :             ShowSevereError(
    3583            2 :                 state, format("ElectricStorage discharge efficiency was too low.  This occurred for electric storage unit named {}", deviceName));
    3584            2 :             ShowContinueError(state, "Please check your input value  for this electric storage unit and fix the discharge efficiency.");
    3585            1 :             errorsFound = true;
    3586            1 :             return minDischargeEfficiency;
    3587              :         } else {
    3588            6 :             return userInputValue;
    3589              :         }
    3590              :     }
    3591              : }
    3592              : 
    3593            3 : void checkChargeDischargeVoltageCurves(
    3594              :     EnergyPlusData &state, std::string_view nameBatt, Real64 const E0c, Real64 const E0d, Curve::Curve *chargeCurve, Curve::Curve *dischargeCurve)
    3595              : {
    3596            3 :     int constexpr numChecks = 50;  // number of divisions for checking the functions from 0 to 1 for fraction charged/discharged
    3597            3 :     int constexpr numReports = 10; // number of divisions for reporting
    3598            3 :     bool gotErrs = false;
    3599              : 
    3600              :     // Fix for Defect #8817.  When the charging curve results in a lower voltage than the discharging curve, the battery
    3601              :     // will give the appearance that the energy in Joules being removed from the battery exceeds what was stored.  This
    3602              :     // checks to make sure that voltage for charging is always higher than discharging at the same fraction charged.
    3603           54 :     for (int loop = 1; loop <= numChecks + 1; ++loop) {
    3604           53 :         Real64 xfc = float(loop - 1) / float(numChecks);                   // = q0/qmax
    3605           53 :         Real64 xfd = 1.0 - xfc;                                            // = (qmax-q0)/qmax = 1 - xfc
    3606           53 :         Real64 chargeVoltage = E0d + chargeCurve->value(state, xfc);       // E0d+Ac*xfc+Cc*xfc/(Dc-xfc)
    3607           53 :         Real64 dischargeVoltage = E0c + dischargeCurve->value(state, xfd); // E0c+Ad*xfd+Cd*xfd/(Dd-xfd)
    3608           53 :         if (dischargeVoltage > chargeVoltage) {
    3609            2 :             gotErrs = true;
    3610            2 :             break;
    3611              :         }
    3612              :     }
    3613            3 :     if (gotErrs) {
    3614            2 :         ShowWarningMessage(state, format("Kinetic Battery Model: {} has a charging/discharging voltage curve conflict.", nameBatt));
    3615            4 :         ShowContinueError(state,
    3616              :                           "Discharging voltage is higher than charging voltage which may potentially lead to an imbalance in the stored energy.");
    3617            4 :         ShowContinueError(state, "Check the charging and discharging curves to make sure that the charging voltage is greater than discharging.");
    3618            4 :         ShowContinueError(state, "Also check the charging and discharging energy outputs to find any discrepancies.");
    3619           24 :         for (int loop = 1; loop <= numReports + 1; ++loop) {
    3620           22 :             Real64 xfc = float(loop - 1) / float(numReports);                  // = q0/qmax
    3621           22 :             Real64 xfd = 1.0 - xfc;                                            // = (qmax-q0)/qmax = 1 - xfc
    3622           22 :             Real64 chargeVoltage = E0d + chargeCurve->value(state, xfc);       // E0d+Ac*xfc+Cc*xfc/(Dc-xfc)
    3623           22 :             Real64 dischargeVoltage = E0c + dischargeCurve->value(state, xfd); // E0c+Ad*xfd+Cd*xfd/(Dd-xfd)
    3624           44 :             ShowContinueError(
    3625              :                 state,
    3626           44 :                 format(
    3627              :                     "Charged fraction = {:.1R}, Charging voltage = {:.3R} V, Discharging voltage = {:.3R} V", xfc, chargeVoltage, dischargeVoltage));
    3628              :         }
    3629              :     }
    3630            3 : }
    3631              : 
    3632            1 : void ElectricStorage::reinitAtBeginEnvironment()
    3633              : {
    3634            1 :     pelNeedFromStorage_ = 0.0;
    3635            1 :     pelFromStorage_ = 0.0;
    3636            1 :     pelIntoStorage_ = 0.0;
    3637            1 :     qdotConvZone_ = 0.0;
    3638            1 :     qdotRadZone_ = 0.0;
    3639            1 :     timeElapsed_ = 0.0;
    3640            1 :     electEnergyinStorage_ = 0.0;
    3641            1 :     storedPower_ = 0.0;
    3642            1 :     storedEnergy_ = 0.0;
    3643            1 :     decrementedEnergyStored_ = 0.0;
    3644            1 :     drawnPower_ = 0.0;
    3645            1 :     drawnEnergy_ = 0.0;
    3646            1 :     thermLossRate_ = 0.0;
    3647            1 :     thermLossEnergy_ = 0.0;
    3648            1 :     lastTimeStepStateOfCharge_ = startingEnergyStored_;
    3649            1 :     thisTimeStepStateOfCharge_ = startingEnergyStored_;
    3650              : 
    3651            1 :     if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3652            0 :         Real64 initialCharge = maxAhCapacity_ * startingSOC_;
    3653            0 :         lastTwoTimeStepAvailable_ = initialCharge * availableFrac_;
    3654            0 :         lastTwoTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3655            0 :         lastTimeStepAvailable_ = initialCharge * availableFrac_;
    3656            0 :         lastTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3657            0 :         thisTimeStepAvailable_ = initialCharge * availableFrac_;
    3658            0 :         thisTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3659            0 :         if (lifeCalculation_) {
    3660            0 :             count0_ = 1;            // Index 0 is for initial SOC, so new input starts from index 1.
    3661            0 :             b10_[0] = startingSOC_; // the initial fractional SOC is stored as the reference
    3662            0 :             x0_[0] = 0.0;
    3663            0 :             for (int loop = 1; loop < maxRainflowArrayBounds_ + 1; ++loop) {
    3664            0 :                 b10_[loop] = 0.0;
    3665            0 :                 x0_[loop] = 0.0;
    3666              :             }
    3667            0 :             for (int loop = 0; loop < cycleBinNum_; ++loop) {
    3668            0 :                 oneNmb0_[loop] = 0.0;
    3669            0 :                 nmb0_[loop] = 0.0;
    3670              :             }
    3671            0 :             batteryDamage_ = 0.0;
    3672              :         }
    3673            1 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3674              :         // Copy the initial battery state to the last battery state
    3675            1 :         *ssc_lastBatteryState_ = *ssc_initBatteryState_;
    3676            1 :         ssc_lastBatteryTimeStep_ = ssc_initBatteryTimeStep_;
    3677            1 :         ssc_battery_->set_state(*ssc_lastBatteryState_, ssc_initBatteryTimeStep_);
    3678              :     }
    3679            1 :     myWarmUpFlag_ = true;
    3680            1 : }
    3681              : 
    3682            0 : void ElectricStorage::reinitZoneGainsAtBeginEnvironment()
    3683              : {
    3684            0 :     qdotConvZone_ = 0.0;
    3685            0 :     qdotRadZone_ = 0.0;
    3686            0 : }
    3687              : 
    3688            1 : void ElectricStorage::reinitAtEndWarmup()
    3689              : {
    3690              :     // need to reset initial state of charge at beginning of environment but after warm up is complete
    3691            1 :     lastTimeStepStateOfCharge_ = startingEnergyStored_;
    3692            1 :     thisTimeStepStateOfCharge_ = startingEnergyStored_;
    3693            1 :     if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3694            0 :         Real64 initialCharge = maxAhCapacity_ * startingSOC_;
    3695            0 :         lastTwoTimeStepAvailable_ = initialCharge * availableFrac_;
    3696            0 :         lastTwoTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3697            0 :         lastTimeStepAvailable_ = initialCharge * availableFrac_;
    3698            0 :         lastTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3699            0 :         thisTimeStepAvailable_ = initialCharge * availableFrac_;
    3700            0 :         thisTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3701            0 :         if (lifeCalculation_) {
    3702            0 :             count0_ = 1;            // Index 0 is for initial SOC, so new input starts from index 1.
    3703            0 :             b10_[0] = startingSOC_; // the initial fractional SOC is stored as the reference
    3704            0 :             x0_[0] = 0.0;
    3705            0 :             for (int loop = 1; loop < maxRainflowArrayBounds_ + 1; ++loop) {
    3706            0 :                 b10_[loop] = 0.0;
    3707            0 :                 x0_[loop] = 0.0;
    3708              :             }
    3709            0 :             for (int loop = 0; loop < cycleBinNum_; ++loop) {
    3710            0 :                 oneNmb0_[loop] = 0.0;
    3711            0 :                 nmb0_[loop] = 0.0;
    3712              :             }
    3713            0 :             batteryDamage_ = 0.0;
    3714              :         }
    3715            1 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3716              :         // Copy the initial battery state to the last battery state
    3717            1 :         *ssc_lastBatteryState_ = *ssc_initBatteryState_;
    3718            1 :         ssc_lastBatteryTimeStep_ = ssc_initBatteryTimeStep_;
    3719            1 :         ssc_battery_->set_state(*ssc_lastBatteryState_, ssc_lastBatteryTimeStep_);
    3720              :     }
    3721            1 :     myWarmUpFlag_ = false;
    3722            1 : }
    3723              : 
    3724            3 : void ElectricStorage::timeCheckAndUpdate(EnergyPlusData &state)
    3725              : {
    3726              : 
    3727            3 :     if (myWarmUpFlag_ && !state.dataGlobal->WarmupFlag) {
    3728            1 :         reinitAtEndWarmup();
    3729              :     }
    3730              : 
    3731              :     Real64 timeElapsedLoc =
    3732            3 :         state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
    3733            3 :     if (timeElapsed_ != timeElapsedLoc) { // time changed, update last with "current" result from previous time
    3734            1 :         if (storageModelMode_ == StorageModelType::KIBaMBattery && lifeCalculation_) {
    3735              :             //    At this point, the current values, last time step values and last two time step values have not been updated, hence:
    3736              :             //    "ThisTimeStep*" actually points to the previous one time step
    3737              :             //    "LastTimeStep*" actually points to the previous two time steps
    3738              :             //    "LastTwoTimeStep" actually points to the previous three time steps
    3739              : 
    3740              :             //      Calculate the fractional SOC change between the "current" time step and the "previous one" time step
    3741            0 :             Real64 deltaSOC1 = thisTimeStepAvailable_ + thisTimeStepBound_ - lastTimeStepAvailable_ - lastTimeStepBound_;
    3742            0 :             deltaSOC1 /= maxAhCapacity_;
    3743              : 
    3744              :             //      Calculate the fractional SOC change between the "previous one" time step and the "previous two" time steps
    3745            0 :             Real64 deltaSOC2 = lastTimeStepAvailable_ + lastTimeStepBound_ - lastTwoTimeStepAvailable_ - lastTwoTimeStepBound_;
    3746            0 :             deltaSOC2 /= maxAhCapacity_;
    3747              : 
    3748              :             //     DeltaSOC2 = 0 may occur at the begining of each simulation environment.
    3749              :             //     DeltaSOC1 * DeltaSOC2 means that the SOC from "LastTimeStep" is a peak or valley. Only peak or valley needs
    3750              :             //     to call the rain flow algorithm
    3751            0 :             if ((deltaSOC2 == 0) || ((deltaSOC1 * deltaSOC2) < 0)) {
    3752              :                 //     Because we cannot determine whehter "ThisTimeStep" is a peak or valley (next time step is unknown yet), we
    3753              :                 //     use the "LastTimeStep" value for battery life calculation.
    3754            0 :                 Real64 input0 = (lastTimeStepAvailable_ + lastTimeStepBound_) / maxAhCapacity_;
    3755            0 :                 b10_[count0_] = input0;
    3756              : 
    3757              :                 //        The array size needs to be increased when count = MaxRainflowArrayBounds. Please note that (MaxRainflowArrayBounds +1)
    3758              :                 //        is the index used in the subroutine RainFlow. So we cannot reallocate array size until count = MaxRainflowArrayBounds +1.
    3759              : 
    3760            0 :                 int constexpr maxRainflowArrayInc_ = 100;
    3761              : 
    3762            0 :                 if (count0_ == maxRainflowArrayBounds_) {
    3763            0 :                     b10_.resize(maxRainflowArrayBounds_ + 1 + maxRainflowArrayInc_, 0.0);
    3764            0 :                     x0_.resize(maxRainflowArrayBounds_ + 1 + maxRainflowArrayInc_, 0.0);
    3765            0 :                     maxRainflowArrayBounds_ += maxRainflowArrayInc_;
    3766              :                 }
    3767              : 
    3768            0 :                 rainflow(cycleBinNum_, input0, b10_, x0_, count0_, nmb0_, oneNmb0_);
    3769              : 
    3770            0 :                 batteryDamage_ = 0.0;
    3771              : 
    3772            0 :                 for (int binNum = 0; binNum < cycleBinNum_; ++binNum) {
    3773              :                     //       Battery damage is calculated by accumulating the impact from each cycle.
    3774            0 :                     batteryDamage_ += oneNmb0_[binNum] / lifeCurve_->value(state, (double(binNum) / double(cycleBinNum_)));
    3775              :                 }
    3776              :             }
    3777            1 :         } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3778            1 :             *ssc_lastBatteryState_ = ssc_battery_->get_state();
    3779            1 :             ssc_lastBatteryTimeStep_ = ssc_battery_->get_params().dt_hr;
    3780              :         }
    3781              : 
    3782            1 :         lastTimeStepStateOfCharge_ = thisTimeStepStateOfCharge_;
    3783            1 :         lastTwoTimeStepAvailable_ = lastTimeStepAvailable_;
    3784            1 :         lastTwoTimeStepBound_ = lastTimeStepBound_;
    3785            1 :         lastTimeStepAvailable_ = thisTimeStepAvailable_;
    3786            1 :         lastTimeStepBound_ = thisTimeStepBound_;
    3787            1 :         timeElapsed_ = timeElapsedLoc;
    3788              : 
    3789              :     } // end if time changed
    3790            3 : }
    3791              : 
    3792            4 : void ElectricStorage::simulate(EnergyPlusData &state,
    3793              :                                Real64 &powerCharge,
    3794              :                                Real64 &powerDischarge,
    3795              :                                bool &charging,
    3796              :                                bool &discharging,
    3797              :                                Real64 const controlSOCMaxFracLimit,
    3798              :                                Real64 const controlSOCMinFracLimit)
    3799              : {
    3800              :     // pass thru to constrain function depending on storage model type
    3801            4 :     if (availSched_->getCurrentVal() == 0.0) { // storage not available
    3802            0 :         discharging = false;
    3803            0 :         powerDischarge = 0.0;
    3804            0 :         charging = false;
    3805            0 :         powerCharge = 0.0;
    3806              :     }
    3807              : 
    3808            4 :     if (storageModelMode_ == StorageModelType::SimpleBucketStorage) {
    3809            0 :         simulateSimpleBucketModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3810            4 :     } else if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3811            0 :         simulateKineticBatteryModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3812            4 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3813            4 :         simulateLiIonNmcBatteryModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3814              :     }
    3815            4 : }
    3816              : 
    3817            1 : std::string const &ElectricStorage::name() const
    3818              : {
    3819            1 :     return name_;
    3820              : }
    3821              : 
    3822            0 : void ElectricStorage::simulateSimpleBucketModel(EnergyPlusData &state,
    3823              :                                                 Real64 &powerCharge,
    3824              :                                                 Real64 &powerDischarge,
    3825              :                                                 bool &charging,
    3826              :                                                 bool &discharging,
    3827              :                                                 Real64 const controlSOCMaxFracLimit,
    3828              :                                                 Real64 const controlSOCMinFracLimit)
    3829              : {
    3830              : 
    3831              :     // given arguments for how the storage operation would like to run storage charge or discharge
    3832              :     // apply model constraints and adjust arguments accordingly
    3833              : 
    3834            0 :     if (charging) {
    3835              : 
    3836            0 :         if (lastTimeStepStateOfCharge_ >= (maxEnergyCapacity_ * controlSOCMaxFracLimit)) {
    3837              :             // storage full!  no more allowed!
    3838            0 :             powerCharge = 0.0;
    3839            0 :             charging = false;
    3840              :         }
    3841            0 :         if (powerCharge > maxPowerStore_) {
    3842            0 :             powerCharge = maxPowerStore_;
    3843              :         }
    3844              : 
    3845              :         // now check to see if charge would exceed capacity, and modify to just fill physical storage cap
    3846            0 :         if ((lastTimeStepStateOfCharge_ + powerCharge * state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_) >=
    3847            0 :             (maxEnergyCapacity_ * controlSOCMaxFracLimit)) {
    3848            0 :             powerCharge = ((maxEnergyCapacity_ * controlSOCMaxFracLimit) - lastTimeStepStateOfCharge_) /
    3849            0 :                           (state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_);
    3850              :         }
    3851              :     } // charging
    3852              : 
    3853            0 :     if (discharging) {
    3854              : 
    3855            0 :         if (lastTimeStepStateOfCharge_ <= (maxEnergyCapacity_ * controlSOCMinFracLimit)) {
    3856              :             // storage empty  no more allowed!
    3857            0 :             powerDischarge = 0.0;
    3858            0 :             discharging = false;
    3859              :         }
    3860            0 :         if (powerDischarge > maxPowerDraw_) {
    3861            0 :             powerDischarge = maxPowerDraw_;
    3862              :         }
    3863              :         // now check if will empty this timestep, power draw is amplified by energetic effic
    3864            0 :         if ((lastTimeStepStateOfCharge_ - powerDischarge * state.dataHVACGlobal->TimeStepSysSec / energeticEfficDischarge_) <=
    3865            0 :             (maxEnergyCapacity_ * controlSOCMinFracLimit)) {
    3866            0 :             powerDischarge = (lastTimeStepStateOfCharge_ - (maxEnergyCapacity_ * controlSOCMinFracLimit)) * energeticEfficDischarge_ /
    3867            0 :                              (state.dataHVACGlobal->TimeStepSysSec);
    3868              :         }
    3869              :     }
    3870              : 
    3871            0 :     if ((!charging) && (!discharging)) {
    3872            0 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_;
    3873            0 :         pelIntoStorage_ = 0.0;
    3874            0 :         pelFromStorage_ = 0.0;
    3875              :     }
    3876            0 :     if (charging) {
    3877            0 :         pelIntoStorage_ = powerCharge;
    3878            0 :         pelFromStorage_ = 0.0;
    3879            0 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_ + powerCharge * state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_;
    3880              :     }
    3881            0 :     if (discharging) {
    3882            0 :         pelIntoStorage_ = 0.0;
    3883            0 :         pelFromStorage_ = powerDischarge;
    3884            0 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_ - powerDischarge * state.dataHVACGlobal->TimeStepSysSec / energeticEfficDischarge_;
    3885            0 :         thisTimeStepStateOfCharge_ = max(thisTimeStepStateOfCharge_, 0.0);
    3886              :     }
    3887              : 
    3888              :     // updates and reports
    3889            0 :     electEnergyinStorage_ = thisTimeStepStateOfCharge_; //[J]
    3890            0 :     storedPower_ = pelIntoStorage_;
    3891            0 :     storedEnergy_ = pelIntoStorage_ * state.dataHVACGlobal->TimeStepSysSec;
    3892            0 :     decrementedEnergyStored_ = -1.0 * storedEnergy_;
    3893            0 :     drawnPower_ = pelFromStorage_;
    3894            0 :     drawnEnergy_ = pelFromStorage_ * state.dataHVACGlobal->TimeStepSysSec;
    3895            0 :     thermLossRate_ = max(storedPower_ * (1.0 - energeticEfficCharge_), drawnPower_ * (1.0 - energeticEfficDischarge_));
    3896            0 :     thermLossEnergy_ = thermLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    3897              : 
    3898            0 :     if (zoneNum_ > 0) { // set values for zone heat gains
    3899            0 :         qdotConvZone_ = (1.0 - zoneRadFract_) * thermLossRate_;
    3900            0 :         qdotRadZone_ = (zoneRadFract_)*thermLossRate_;
    3901              :     }
    3902            0 : }
    3903              : 
    3904            0 : void ElectricStorage::simulateKineticBatteryModel(EnergyPlusData &state,
    3905              :                                                   Real64 &powerCharge,
    3906              :                                                   Real64 &powerDischarge,
    3907              :                                                   bool &charging,
    3908              :                                                   bool &discharging,
    3909              :                                                   Real64 const controlSOCMaxFracLimit,
    3910              :                                                   Real64 const controlSOCMinFracLimit)
    3911              : {
    3912              : 
    3913              :     // initialize locals
    3914            0 :     Real64 I0 = 0.0;
    3915            0 :     Real64 Volt = 0.0;
    3916              : 
    3917            0 :     Real64 T0 = 0.0;
    3918            0 :     Real64 qmaxf = 0.0;
    3919            0 :     Real64 Ef = 0.0;
    3920            0 :     Real64 q0 = 0.0;
    3921              : 
    3922            0 :     Real64 qmax = maxAhCapacity_;
    3923            0 :     Real64 E0c = chargedOCV_;
    3924            0 :     Real64 E0d = dischargedOCV_;
    3925            0 :     Real64 k = chargeConversionRate_;
    3926            0 :     Real64 c = availableFrac_;
    3927              : 
    3928            0 :     if (charging) {
    3929              : 
    3930              :         //*************************************************
    3931              :         // The sign of power and current is negative in charging
    3932              :         //*************************************************
    3933            0 :         Real64 Pw = -powerCharge / numBattery_;
    3934            0 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    3935            0 :         if (q0 > qmax * controlSOCMaxFracLimit) {
    3936              :             // stop charging with controller signal for max state of charge
    3937            0 :             Pw = 0.0;
    3938            0 :             powerCharge = 0.0;
    3939            0 :             charging = false;
    3940            0 :             storageMode_ = 0;
    3941            0 :             storedPower_ = 0.0;
    3942            0 :             storedEnergy_ = 0.0;
    3943            0 :             decrementedEnergyStored_ = 0.0;
    3944            0 :             drawnPower_ = 0.0;
    3945            0 :             drawnEnergy_ = 0.0;
    3946            0 :             return;
    3947              :         }
    3948              : 
    3949            0 :         I0 = 1.0;                                                                                       // Initial assumption
    3950            0 :         T0 = std::abs(qmax / I0);                                                                       // Initial Assumption
    3951            0 :         qmaxf = qmax * k * c * T0 / (1.0 - std::exp(-k * T0) + c * (k * T0 - 1.0 + std::exp(-k * T0))); // Initial calculation of a function qmax(I)
    3952            0 :         Real64 Xf = q0 / qmaxf;
    3953            0 :         Ef = E0d + chargeCurve_->value(state, Xf); // E0d+Ac*Xf+Cc*Xf/(Dc-Xf) (use curve)
    3954            0 :         Volt = Ef - I0 * internalR_;
    3955            0 :         Real64 Inew = 0.0;
    3956            0 :         if (Volt != 0.0) {
    3957            0 :             Inew = Pw / Volt;
    3958              :         }
    3959            0 :         Real64 Tnew = 0.0;
    3960            0 :         if (Inew != 0.0) {
    3961            0 :             Tnew = qmaxf / std::abs(Inew);
    3962              :         }
    3963            0 :         Real64 error = 1.0;
    3964              : 
    3965            0 :         while (error > 0.0001) { // Iteration process to get converged current(I)
    3966            0 :             I0 = Inew;
    3967            0 :             T0 = Tnew;
    3968            0 :             qmaxf = qmax * k * c * T0 / (1.0 - std::exp(-k * T0) + c * (k * T0 - 1.0 + std::exp(-k * T0)));
    3969            0 :             Xf = q0 / qmaxf;
    3970            0 :             Ef = E0d + chargeCurve_->value(state, Xf); // E0d+Ac*Xf+Cc*Xf/(Dc-Xf) (use curve)
    3971            0 :             Volt = Ef - I0 * internalR_;
    3972            0 :             Inew = Pw / Volt;
    3973            0 :             Tnew = std::abs(qmaxf / Inew); // ***Always positive here
    3974            0 :             error = std::abs(Inew - I0);
    3975              :         }
    3976              : 
    3977            0 :         Real64 dividend = -k * c * qmax + k * lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    3978            0 :                           q0 * k * c * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    3979            0 :         Real64 divisor = 1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    3980            0 :                          c * (k * state.dataHVACGlobal->TimeStepSys - 1 + std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    3981            0 :         Real64 Imax = dividend / divisor;
    3982              :         // Below: This is the limit of charging current from Charge Rate Limit (input)
    3983            0 :         Imax = max(Imax, -(qmax - q0) * maxChargeRate_);
    3984              : 
    3985            0 :         if (std::abs(I0) <= std::abs(Imax)) {
    3986            0 :             I0 = Pw / Volt;
    3987              :             // Pactual = I0 * Volt;
    3988              :         } else {
    3989            0 :             I0 = Imax;
    3990            0 :             qmaxf = 80.0; // Initial assumption to solve the equation using iterative method
    3991            0 :             error = 10.0; // Initial assumption ...
    3992            0 :             while (error > 0.001) {
    3993              :                 // *** I0(current) should be positive for this calculation
    3994            0 :                 Real64 RHS = (qmax * k * c * qmaxf / std::abs(I0)) /
    3995            0 :                              (1.0 - std::exp(-k * qmaxf / std::abs(I0)) + c * (k * qmaxf / std::abs(I0) - 1.0 + std::exp(-k * qmaxf / std::abs(I0))));
    3996            0 :                 error = std::abs(qmaxf - RHS);
    3997            0 :                 qmaxf = RHS;
    3998              :             }
    3999              :         }
    4000              :     }
    4001              : 
    4002            0 :     if (discharging) {
    4003              :         //**********************************************
    4004              :         // The sign of power and current is positive in discharging
    4005              :         //**********************************************
    4006              : 
    4007            0 :         Real64 Pw = powerDischarge / numBattery_;
    4008            0 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    4009              : 
    4010            0 :         if (q0 < qmax * controlSOCMinFracLimit) {
    4011              :             // stop discharging with controller signal for min state of charge
    4012            0 :             Pw = 0.0;
    4013            0 :             discharging = false;
    4014            0 :             powerDischarge = 0.0;
    4015            0 :             storageMode_ = 0;
    4016            0 :             storedPower_ = 0.0;
    4017            0 :             storedEnergy_ = 0.0;
    4018            0 :             decrementedEnergyStored_ = 0.0;
    4019            0 :             drawnPower_ = 0.0;
    4020            0 :             drawnEnergy_ = 0.0;
    4021            0 :             return;
    4022              :         }
    4023              : 
    4024            0 :         bool const ok = determineCurrentForBatteryDischarge(state, I0, T0, Volt, Pw, q0, dischargeCurve_, k, c, qmax, E0c, internalR_);
    4025            0 :         if (!ok) {
    4026            0 :             ShowFatalError(state,
    4027            0 :                            format("ElectricLoadCenter:Storage:Battery named=\"{}\". Battery discharge current could not be estimated due to "
    4028              :                                   "iteration limit reached. ",
    4029            0 :                                   name_));
    4030              :             // issue #5301, need more diagnostics for this.
    4031              :         }
    4032              : 
    4033            0 :         Real64 dividend = k * lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4034            0 :                           q0 * k * c * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    4035            0 :         Real64 divisor = 1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4036            0 :                          c * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    4037            0 :         Real64 Imax = dividend / divisor;
    4038            0 :         Imax = min(Imax, maxDischargeI_);
    4039            0 :         if (std::abs(I0) <= Imax) {
    4040            0 :             I0 = Pw / Volt;
    4041              :             // Pactual = I0 * Volt;
    4042              :         } else {
    4043            0 :             I0 = Imax;
    4044            0 :             qmaxf = 10.0;        // Initial assumption to solve the equation using iterative method
    4045            0 :             Real64 error = 10.0; // Initial assumption ...
    4046            0 :             while (error > 0.001) {
    4047            0 :                 Real64 RHS = (qmax * k * c * qmaxf / I0) / (1.0 - std::exp(-k * qmaxf / I0) + c * (k * qmaxf / I0 - 1 + std::exp(-k * qmaxf / I0)));
    4048            0 :                 error = std::abs(qmaxf - RHS);
    4049            0 :                 qmaxf = RHS;
    4050              :             }
    4051            0 :             Real64 Xf = (qmax - q0) / qmaxf;
    4052            0 :             Ef = E0c + dischargeCurve_->value(state, Xf);
    4053            0 :             Volt = Ef - I0 * internalR_;
    4054              :         }
    4055            0 :         if (Volt < cutoffV_) {
    4056            0 :             I0 = 0.0;
    4057              :         }
    4058              :     } // if discharging
    4059              : 
    4060            0 :     if ((!charging) && (!discharging)) {
    4061            0 :         thisTimeStepAvailable_ = lastTimeStepAvailable_;
    4062            0 :         thisTimeStepBound_ = lastTimeStepBound_;
    4063            0 :         I0 = 0.0;
    4064            0 :         Volt = 0.0;
    4065            0 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    4066              :     } else {
    4067            0 :         Real64 newAvailable = lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4068            0 :                               (q0 * k * c - I0) * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k -
    4069            0 :                               I0 * c * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k;
    4070            0 :         Real64 newBound = lastTimeStepBound_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4071            0 :                           q0 * (1.0 - c) * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys)) -
    4072            0 :                           I0 * (1.0 - c) * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k;
    4073            0 :         thisTimeStepAvailable_ = max(0.0, newAvailable);
    4074            0 :         thisTimeStepBound_ = max(0.0, newBound);
    4075              :     }
    4076              : 
    4077              :     // Pactual = I0 * Volt;
    4078            0 :     Real64 TotalSOC = thisTimeStepAvailable_ + thisTimeStepBound_;
    4079              : 
    4080              :     // output1
    4081            0 :     if (TotalSOC > q0) {
    4082            0 :         storageMode_ = 2;
    4083            0 :         storedPower_ = -1.0 * Volt * I0 * numBattery_; // Issue #5303, fix sign issue
    4084            0 :         storedEnergy_ = -1.0 * Volt * I0 * numBattery_ * state.dataHVACGlobal->TimeStepSysSec;
    4085            0 :         decrementedEnergyStored_ = -1.0 * storedEnergy_;
    4086            0 :         drawnPower_ = 0.0;
    4087            0 :         drawnEnergy_ = 0.0;
    4088              : 
    4089            0 :     } else if (TotalSOC < q0) {
    4090            0 :         storageMode_ = 1;
    4091            0 :         storedPower_ = 0.0;
    4092            0 :         storedEnergy_ = 0.0;
    4093            0 :         decrementedEnergyStored_ = 0.0;
    4094            0 :         drawnPower_ = Volt * I0 * numBattery_;
    4095            0 :         drawnEnergy_ = Volt * I0 * numBattery_ * state.dataHVACGlobal->TimeStepSysSec;
    4096              : 
    4097              :     } else {
    4098            0 :         storageMode_ = 0;
    4099            0 :         storedPower_ = 0.0;
    4100            0 :         storedEnergy_ = 0.0;
    4101            0 :         decrementedEnergyStored_ = 0.0;
    4102            0 :         drawnPower_ = 0.0;
    4103            0 :         drawnEnergy_ = 0.0;
    4104              :     }
    4105              : 
    4106            0 :     absoluteSOC_ = TotalSOC * numBattery_;
    4107            0 :     fractionSOC_ = TotalSOC / qmax;
    4108            0 :     batteryCurrent_ = I0 * parallelNum_;
    4109            0 :     batteryVoltage_ = Volt * seriesNum_;
    4110            0 :     thermLossRate_ = internalR_ * pow_2(I0) * numBattery_;
    4111            0 :     thermLossEnergy_ = internalR_ * pow_2(I0) * state.dataHVACGlobal->TimeStepSysSec * numBattery_;
    4112              : 
    4113            0 :     if (zoneNum_ > 0) { // set values for zone heat gains
    4114            0 :         qdotConvZone_ = ((1.0 - zoneRadFract_) * thermLossRate_) * numBattery_;
    4115            0 :         qdotRadZone_ = ((zoneRadFract_)*thermLossRate_) * numBattery_;
    4116              :     }
    4117              : 
    4118            0 :     powerCharge = storedPower_;
    4119            0 :     powerDischarge = drawnPower_;
    4120              : }
    4121              : 
    4122            4 : void ElectricStorage::simulateLiIonNmcBatteryModel(EnergyPlusData &state,
    4123              :                                                    Real64 &powerCharge,
    4124              :                                                    Real64 &powerDischarge,
    4125              :                                                    bool &charging,
    4126              :                                                    bool &discharging,
    4127              :                                                    Real64 const controlSOCMaxFracLimit,
    4128              :                                                    Real64 const controlSOCMinFracLimit)
    4129              : {
    4130              : 
    4131              : // Disable floating point exceptions around SSC battery calculations, which uses quiet_NaN in particular
    4132              : #ifdef DEBUG_ARITHM_GCC_OR_CLANG
    4133            4 :     int old_excepts = fegetexcept();
    4134            4 :     fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
    4135              : #endif
    4136              : 
    4137              : #ifdef DEBUG_ARITHM_MSVC
    4138              :     unsigned int ori_fpcntrl = 0;
    4139              :     _controlfp_s(&ori_fpcntrl, 0, 0);
    4140              : 
    4141              :     // Temporarily disable traps, as MSVC does it reversed compared to GCC,
    4142              :     // you need to turn ON the bits for the exceptions you want to mask
    4143              :     unsigned int fpcntrl = ori_fpcntrl | (_EM_ZERODIVIDE | _EM_INVALID | _EM_OVERFLOW);
    4144              :     _controlfp_s(&fpcntrl, fpcntrl, _MCW_EM);
    4145              : #endif
    4146              : 
    4147              :     // Copy the battery state from the end of last timestep
    4148            4 :     battery_state battState = *ssc_lastBatteryState_;
    4149            4 :     ssc_battery_->set_state(battState, ssc_lastBatteryTimeStep_);
    4150            4 :     if (std::lround(ssc_battery_->get_params().dt_hr * 60.0) != std::lround(state.dataHVACGlobal->TimeStepSys * 60.0)) {
    4151            0 :         ssc_battery_->ChangeTimestep(state.dataHVACGlobal->TimeStepSys);
    4152              :     }
    4153              : 
    4154              :     // Set the temperature the battery sees
    4155            4 :     if (zoneNum_ > 0) {
    4156              :         // If in a zone, use the zone temperature
    4157            0 :         battState.thermal->T_room = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum_).ZT;
    4158              :     } else {
    4159              :         // If outside, use outdoor temperature
    4160            4 :         battState.thermal->T_room = state.dataEnvrn->OutDryBulbTemp;
    4161              :     }
    4162              : 
    4163              :     // Set the SOC limits
    4164            4 :     ssc_battery_->changeSOCLimits(controlSOCMinFracLimit * 100.0, controlSOCMaxFracLimit * 100.0);
    4165              : 
    4166              :     // Set the current timestep length
    4167              : 
    4168              :     // Run the battery
    4169              :     // SAM uses negative values for charging, positive for discharging
    4170              :     // E+ power/energy outputs are positive
    4171            4 :     double power{0.0}; // Using double instead of Real64 because SSC is expecting a double
    4172            4 :     if (charging) {
    4173            1 :         power = -powerCharge;
    4174            3 :     } else if (discharging) {
    4175            2 :         power = powerDischarge;
    4176              :     }
    4177            4 :     power *= 0.001; // Convert to kW
    4178            4 :     ssc_battery_->runPower(power);
    4179              : 
    4180              :     // Store outputs
    4181            4 :     const battery_state &battState2{ssc_battery_->get_state()};
    4182            4 :     if (battState2.P < 0.0) { // negative for charging
    4183            1 :         storageMode_ = 2;
    4184            1 :         powerCharge = fabs(battState2.P) * 1000.0; // kW -> W
    4185            1 :         powerDischarge = 0.0;
    4186            1 :         charging = true;
    4187            1 :         discharging = false;
    4188            3 :     } else if (battState2.P > 0.0) { // positive for discharging
    4189            2 :         storageMode_ = 1;
    4190            2 :         powerCharge = 0.0;
    4191            2 :         powerDischarge = fabs(battState2.P) * 1000.0; // kW -> W
    4192            2 :         charging = false;
    4193            2 :         discharging = true;
    4194              :     } else {
    4195            1 :         storageMode_ = 0;
    4196            1 :         powerCharge = 0.0;
    4197            1 :         powerDischarge = 0.0;
    4198            1 :         charging = false;
    4199            1 :         discharging = false;
    4200              :     }
    4201            4 :     absoluteSOC_ = ssc_battery_->charge_total();
    4202            4 :     fractionSOC_ = ssc_battery_->SOC() * 0.01; // % -> fraction
    4203            4 :     batteryCurrent_ = ssc_battery_->I();
    4204            4 :     batteryVoltage_ = ssc_battery_->V();
    4205            4 :     batteryDamage_ = 1.0 - (ssc_battery_->charge_maximum_lifetime() / maxAhCapacity_);
    4206            4 :     storedPower_ = powerCharge;
    4207            4 :     storedEnergy_ = storedPower_ * state.dataHVACGlobal->TimeStepSysSec;
    4208            4 :     drawnPower_ = powerDischarge;
    4209            4 :     drawnEnergy_ = drawnPower_ * state.dataHVACGlobal->TimeStepSysSec;
    4210            4 :     decrementedEnergyStored_ = -storedEnergy_;
    4211            4 :     thermLossRate_ = battState2.thermal->heat_dissipated * 1000.0; // kW -> W
    4212            4 :     thermLossEnergy_ = thermLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4213            4 :     batteryTemperature_ = battState2.thermal->T_batt;
    4214              : 
    4215              :     // Zone Heat Gains
    4216            4 :     if (zoneNum_ > 0) { // set values for zone heat gains
    4217            0 :         qdotConvZone_ = (1.0 - zoneRadFract_) * thermLossRate_;
    4218            0 :         qdotRadZone_ = (zoneRadFract_)*thermLossRate_;
    4219              :     }
    4220              : 
    4221              : #ifdef DEBUG_ARITHM_GCC_OR_CLANG
    4222            4 :     feenableexcept(old_excepts);
    4223              : #endif
    4224              : 
    4225              : #ifdef DEBUG_ARITHM_MSVC
    4226              :     _controlfp_s(nullptr, ori_fpcntrl, _MCW_EM);
    4227              : #endif
    4228            4 : }
    4229              : 
    4230            2 : Real64 ElectricStorage::drawnPower() const
    4231              : {
    4232            2 :     return drawnPower_;
    4233              : }
    4234              : 
    4235            2 : Real64 ElectricStorage::storedPower() const
    4236              : {
    4237            2 :     return storedPower_;
    4238              : }
    4239              : 
    4240            2 : Real64 ElectricStorage::drawnEnergy() const
    4241              : {
    4242            2 :     return drawnEnergy_;
    4243              : }
    4244              : 
    4245            2 : Real64 ElectricStorage::storedEnergy() const
    4246              : {
    4247            2 :     return storedEnergy_;
    4248              : }
    4249              : 
    4250            4 : Real64 ElectricStorage::stateOfChargeFraction() const
    4251              : {
    4252            4 :     return fractionSOC_;
    4253              : }
    4254              : 
    4255            1 : Real64 ElectricStorage::batteryTemperature() const
    4256              : {
    4257            1 :     assert(storageModelMode_ == StorageModelType::LiIonNmcBattery);
    4258            1 :     return batteryTemperature_;
    4259              : }
    4260              : 
    4261            2 : bool ElectricStorage::determineCurrentForBatteryDischarge(EnergyPlusData &state,
    4262              :                                                           Real64 &curI0,
    4263              :                                                           Real64 &curT0,
    4264              :                                                           Real64 &curVolt,
    4265              :                                                           Real64 const Pw, // Power withdraw from each module,
    4266              :                                                           Real64 const q0, // available charge last timestep, sum of available and bound
    4267              :                                                           Curve::Curve *curve,
    4268              :                                                           Real64 const k,
    4269              :                                                           Real64 const c,
    4270              :                                                           Real64 const qmax,
    4271              :                                                           Real64 const E0c,
    4272              :                                                           Real64 const InternalR)
    4273              : {
    4274            2 :     curI0 = 10.0;         // Initial assumption
    4275            2 :     curT0 = qmax / curI0; // Initial Assumption
    4276            2 :     Real64 qmaxf = qmax * k * c * curT0 /
    4277            2 :                    (1.0 - std::exp(-k * curT0) + c * (k * curT0 - 1.0 + std::exp(-k * curT0))); // Initial calculation of a function qmax(I)
    4278            2 :     Real64 Xf = (qmax - q0) / qmaxf;
    4279            2 :     Real64 Ef = E0c + curve->value(state, Xf); // E0d+Ac*Xf+Cc*X/(Dc-Xf)
    4280            2 :     curVolt = Ef - curI0 * InternalR;
    4281            2 :     Real64 Inew = Pw / curVolt;
    4282            2 :     Real64 Tnew = qmaxf / Inew;
    4283            2 :     Real64 error = 1.0;
    4284            2 :     int countForIteration = 0;
    4285            2 :     bool exceedIterationLimit = false;
    4286              : 
    4287         1004 :     while (error > 0.0001) { // Iteration process to get converged current(I)
    4288         1003 :         curI0 = Inew;
    4289         1003 :         curT0 = Tnew;
    4290         1003 :         qmaxf = qmax * k * c * curT0 / (1.0 - std::exp(-k * curT0) + c * (k * curT0 - 1.0 + std::exp(-k * curT0)));
    4291              :         // add div by zero protection #5301
    4292         1003 :         if (qmaxf != 0.0) {
    4293         1003 :             Xf = (qmax - q0) / qmaxf;
    4294              :         } else {
    4295            0 :             Xf = 1.0;
    4296              :         }
    4297              : 
    4298         1003 :         Ef = E0c + curve->value(state, Xf); // E0c+Ad*Xf+Cd*X/(Dd-Xf)
    4299         1003 :         curVolt = Ef - curI0 * InternalR;
    4300              :         // add div by zero protection #5301
    4301         1003 :         if (curVolt != 0.0) {
    4302         1003 :             Inew = Pw / curVolt;
    4303              :         } else {
    4304            0 :             Inew = 1.0;
    4305              :         }
    4306              : 
    4307              :         // add div by zero protection #5301
    4308         1003 :         if (Inew != 0.0) {
    4309         1003 :             Tnew = qmaxf / Inew;
    4310              :         } else {
    4311            0 :             Tnew = 1.0;
    4312              :         }
    4313              : 
    4314         1003 :         error = std::abs(Inew - curI0);
    4315         1003 :         ++countForIteration;
    4316         1003 :         if (countForIteration > 1000) {
    4317            1 :             exceedIterationLimit = true;
    4318              :             // Issue #5301 need more diagnostics for this case
    4319            2 :             ShowWarningError(
    4320              :                 state, "ElectricStorage::determineCurrentForBatteryDischarge, iteration limit exceeded, failed to solve for discharge current.");
    4321            1 :             ShowContinueError(state, format("Last timestep charge available, q0 = {:.5R}", q0));
    4322            1 :             ShowContinueError(state, format("New Current, Inew = {:.5R} [Amps]", Inew));
    4323            1 :             ShowContinueError(state, format("Power discharge per module cell, Pw = {:.5R} ", Pw));
    4324            2 :             ShowContinueError(
    4325            2 :                 state, format("Charge Conversion Rate, [1/h] change rate from bound charge energy to available charge, parameter k = {:.5R}", k));
    4326            1 :             ShowContinueError(state, format("parameter c = {:.5R}", c));
    4327            1 :             ShowContinueError(state, format("parameter qmax = {:.5R}", qmax));
    4328            1 :             ShowContinueError(state, format("Fully charged open circuit voltage, parameter E0c  = {:.5R}", E0c));
    4329            1 :             ShowContinueError(state, format("parameter InternalR = {:.5R}", InternalR));
    4330            1 :             if (qmaxf == 0.0) {
    4331            0 :                 ShowContinueError(state, "qmaxf was zero, would have divided by zero.");
    4332              :             }
    4333            1 :             if (Inew == 0.0) {
    4334            0 :                 ShowContinueError(state, "Inew was zero, would have divided by zero. ");
    4335              :             }
    4336            1 :             if (curVolt == 0.0) {
    4337            0 :                 ShowContinueError(state, "curVolt was zero, would have divided by zero. ");
    4338              :             }
    4339              : 
    4340            2 :             ShowContinueErrorTimeStamp(state, "ElectricStorage::determineCurrentForBatteryDischarge ");
    4341            1 :             break;
    4342              :         }
    4343              :     }
    4344            2 :     return (!exceedIterationLimit);
    4345              : }
    4346              : 
    4347            0 : void ElectricStorage::rainflow(int const numbin,           // numbin = constant value
    4348              :                                Real64 const input,         // input = input value from other object (battery model)
    4349              :                                std::vector<Real64> &B1,    // stores values of points, calculated here - stored for next timestep
    4350              :                                std::vector<Real64> &X,     // stores values of two data point difference, calculated here - stored for next timestep
    4351              :                                int &count,                 // calculated here - stored for next timestep in main loop
    4352              :                                std::vector<Real64> &Nmb,   // calculated here - stored for next timestep in main loop
    4353              :                                std::vector<Real64> &OneNmb // calculated here - stored for next timestep in main loop
    4354              : )
    4355              : {
    4356              :     // SUBROUTINE INFORMATION:
    4357              :     //       AUTHOR         Y. KyungTae & W. Wang
    4358              :     //       DATE WRITTEN   July-August, 2011
    4359              :     //       MODIFIED       na
    4360              :     //       RE-ENGINEERED  na
    4361              : 
    4362              :     // PURPOSE OF THIS SUBROUTINE:
    4363              :     // Rainflow cycle counting for battery life calculation
    4364              : 
    4365              :     // METHODOLOGY EMPLOYED:
    4366              :     // <description>
    4367              : 
    4368              :     // REFERENCES:
    4369              :     // Ariduru S. 2004. Fatigue life calculation by rainflow cycle counting method.
    4370              :     //                  Master Thesis, Middle East Technical University.
    4371              : 
    4372              :     // Locals
    4373              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    4374              :     // Array B1 stores the value of points
    4375              :     // Array X stores the value of two data points' difference.
    4376              : 
    4377              :     int num;
    4378              : 
    4379            0 :     X[count] = input - B1[count - 1]; // calculate the difference between two data (current and previous)
    4380              : 
    4381              :     // Get rid of the data if it is not peak nor valley
    4382              :     // The value of count means the number of peak or valley points added to the arrary B10/B1, not including the
    4383              :     // first point B10(0)/B1(0). Therefore, even if count =2, B1(count-2) is still valid.
    4384            0 :     if (count >= 3) {
    4385              :         //  The following check on peak or valley may be not necessary in most times because the same check is made in the
    4386              :         //  upper-level subroutine. However, it does not hurt to leave it here.
    4387            0 :         if (X[count] * X[count - 1] >= 0) {
    4388            0 :             X[count - 1] = B1[count] - B1[count - 2];
    4389            0 :             shift(B1, count - 1, count, B1); // Get rid of (count-1) row in B1
    4390            0 :             shift(X, count, count, X);
    4391            0 :             --count; // If the value keep increasing or decreasing, get rid of the middle point.
    4392              :         } // Only valley and peak will be stored in the matrix, B1
    4393              : 
    4394            0 :         if ((count == 3) && (std::abs(X[2]) <= std::abs(X[3]))) {
    4395              :             //  This means the starting point is included in X(2), a half cycle is counted according to the rain flow
    4396              :             //  algorithm specified in the reference (Ariduru S. 2004)
    4397            0 :             num = nint((std::abs(X[2]) * numbin * 10 + 5) / 10); // Count half cycle
    4398            0 :             Nmb[num] += 0.5;
    4399              :             // B1 = eoshift( B1, 1 ); // Once counting a half cycle, get rid of the value.
    4400            0 :             B1.erase(B1.begin());
    4401            0 :             B1.push_back(0.0);
    4402              :             // X = eoshift( X, 1 );
    4403            0 :             X.erase(X.begin());
    4404            0 :             X.push_back(0.0);
    4405            0 :             --count; // The number of matrix, B1 and X1 decrease.
    4406              :         }
    4407              :     } // Counting cyle end
    4408              :     //*** Note: The value of "count" changes in the upper "IF LOOP"
    4409              : 
    4410            0 :     if (count >= 4) { // count 1 cycle
    4411            0 :         while (std::abs(X[count]) > std::abs(X[count - 1])) {
    4412              :             //  This means that the starting point is not included in X(count-1). a cycle is counted according to the rain flow
    4413              :             //  algorithm specified in the reference (Ariduru S. 2004)
    4414            0 :             num = nint((std::abs(X[count - 1]) * numbin * 10 + 5) / 10);
    4415            0 :             ++Nmb[num];
    4416              : 
    4417              :             //     X(count-2) = ABS(X(count))-ABS(X(count-1))+ABS(X(count-2))
    4418            0 :             X[count - 2] = B1[count] - B1[count - 3]; // Updating X needs to be done before shift operation below
    4419              : 
    4420            0 :             shift(B1, count - 1, count, B1); // Get rid of two data points one by one
    4421            0 :             shift(B1, count - 2, count, B1); // Delete one point
    4422              : 
    4423            0 :             shift(X, count, count, X);     // Get rid of two data points one by one
    4424            0 :             shift(X, count - 1, count, X); // Delete one point
    4425              : 
    4426            0 :             count -= 2; // If one cycle is counted, two data points are deleted.
    4427            0 :             if (count < 4) {
    4428            0 :                 break; // When only three data points exists, one cycle cannot be counted.
    4429              :             }
    4430              :         }
    4431              :     }
    4432              : 
    4433            0 :     ++count;
    4434              : 
    4435              :     // Check the rest of the half cycles every time step
    4436            0 :     OneNmb = Nmb; // Array Nmb (Bins) will be used for the next time step later.
    4437              :                   // OneNmb is used to show the current output only.
    4438            0 : }
    4439              : 
    4440            0 : void ElectricStorage::shift(std::vector<Real64> &A, int const m, int const n, std::vector<Real64> &B)
    4441              : {
    4442              :     // SUBROUTINE INFORMATION:
    4443              :     //       AUTHOR         Y. KyungTae & W. Wang
    4444              :     //       DATE WRITTEN   July-August, 2011
    4445              :     //       MODIFIED       na
    4446              :     //       RE-ENGINEERED  na
    4447              : 
    4448              :     // PURPOSE OF THIS SUBROUTINE:
    4449              :     // Utility subroutine for rainflow cycle counting
    4450              : 
    4451              :     int ShiftNum; // Loop variable
    4452              : 
    4453            0 :     for (ShiftNum = 1; ShiftNum <= m - 1; ++ShiftNum) {
    4454            0 :         B[ShiftNum] = A[ShiftNum];
    4455              :     }
    4456              : 
    4457            0 :     for (ShiftNum = m; ShiftNum <= n; ++ShiftNum) {
    4458            0 :         B[ShiftNum] = A[ShiftNum + 1];
    4459              :     }
    4460            0 : }
    4461              : 
    4462              : // constructor
    4463            1 : ElectricTransformer::ElectricTransformer(EnergyPlusData &state, std::string const &objectName)
    4464            1 :     : myOneTimeFlag_(true), usageMode_(TransformerUse::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    4465            1 :       zoneRadFrac_(0.0), ratedCapacity_(0.0), factorTempCoeff_(0.0), tempRise_(0.0), eddyFrac_(0.0),
    4466            1 :       performanceInputMode_(TransformerPerformanceInput::Invalid), ratedEfficiency_(0.0), ratedPUL_(0.0), ratedTemp_(0.0), maxPUL_(0.0),
    4467            2 :       considerLosses_(true), ratedNL_(0.0), ratedLL_(0.0), overloadErrorIndex_(0), efficiency_(0.0), powerIn_(0.0), energyIn_(0.0), powerOut_(0.0),
    4468            1 :       energyOut_(0.0), noLoadLossRate_(0.0), noLoadLossEnergy_(0.0), loadLossRate_(0.0), loadLossEnergy_(0.0), thermalLossRate_(0.0),
    4469            1 :       thermalLossEnergy_(0.0), elecUseMeteredUtilityLosses_(0.0), powerConversionMeteredLosses_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0)
    4470              : {
    4471              :     static constexpr std::string_view routineName = "ElectricTransformer constructor ";
    4472            1 :     auto &s_ipsc = state.dataIPShortCut;
    4473              : 
    4474            1 :     bool errorsFound = false;
    4475            1 :     int transformerIDFObjectNum = 0;
    4476            1 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
    4477              : 
    4478            1 :     transformerIDFObjectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Transformer", objectName);
    4479            1 :     if (transformerIDFObjectNum > 0) {
    4480              :         int numAlphas; // Number of elements in the alpha array
    4481              :         int numNums;   // Number of elements in the numeric array
    4482              :         int IOStat;    // IO Status when calling get input subroutine
    4483            2 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    4484            1 :                                                                  s_ipsc->cCurrentModuleObject,
    4485              :                                                                  transformerIDFObjectNum,
    4486            1 :                                                                  s_ipsc->cAlphaArgs,
    4487              :                                                                  numAlphas,
    4488            1 :                                                                  s_ipsc->rNumericArgs,
    4489              :                                                                  numNums,
    4490              :                                                                  IOStat,
    4491            1 :                                                                  s_ipsc->lNumericFieldBlanks,
    4492            1 :                                                                  s_ipsc->lAlphaFieldBlanks,
    4493            1 :                                                                  s_ipsc->cAlphaFieldNames,
    4494            1 :                                                                  s_ipsc->cNumericFieldNames);
    4495              : 
    4496            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    4497            1 :         name_ = s_ipsc->cAlphaArgs(1);
    4498              :         // how to verify names are unique across objects? add to GlobalNames?
    4499            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    4500            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    4501            1 :         } else if ((availSched_ = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    4502            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    4503            0 :             errorsFound = true;
    4504              :         }
    4505              : 
    4506            1 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    4507            0 :             usageMode_ = TransformerUse::PowerInFromGrid; // default
    4508            1 :         } else if ((usageMode_ = static_cast<TransformerUse>(getEnumValue(transformerUseNamesUC, s_ipsc->cAlphaArgs(3)))) ==
    4509              :                    TransformerUse::Invalid) {
    4510            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    4511            0 :             errorsFound = true;
    4512              :         }
    4513              : 
    4514            1 :         if (s_ipsc->lAlphaFieldBlanks(4)) {
    4515            1 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    4516            0 :         } else if ((zoneNum_ = Util::FindItemInList(s_ipsc->cAlphaArgs(4), state.dataHeatBal->Zone)) == 0) {
    4517            0 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    4518            0 :             ShowWarningItemNotFound(
    4519            0 :                 state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4), "Transformer heat losses will not be added to a zone");
    4520              :             // continue with simulation but storage losses not sent to a zone.
    4521              :         } else {
    4522            0 :             heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    4523              :         }
    4524              : 
    4525            1 :         zoneRadFrac_ = s_ipsc->rNumericArgs(1);
    4526            1 :         ratedCapacity_ = s_ipsc->rNumericArgs(2);
    4527              :         // unused phase_ = s_ipsc->rNumericArgs(3);
    4528              : 
    4529            1 :         if (Util::SameString(s_ipsc->cAlphaArgs(5), "Copper")) {
    4530            0 :             factorTempCoeff_ = 234.5;
    4531            1 :         } else if (Util::SameString(s_ipsc->cAlphaArgs(5), "Aluminum")) {
    4532            1 :             factorTempCoeff_ = 225.0;
    4533              :         } else {
    4534            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
    4535            0 :             errorsFound = true;
    4536              :         }
    4537            1 :         tempRise_ = s_ipsc->rNumericArgs(4);
    4538            1 :         eddyFrac_ = s_ipsc->rNumericArgs(5);
    4539              : 
    4540            1 :         performanceInputMode_ = static_cast<TransformerPerformanceInput>(getEnumValue(transformerPerformanceInputNamesUC, s_ipsc->cAlphaArgs(6)));
    4541            1 :         if (performanceInputMode_ == TransformerPerformanceInput::Invalid) {
    4542            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
    4543            0 :             errorsFound = true;
    4544              :         }
    4545              : 
    4546            1 :         if (ratedCapacity_ == 0) {
    4547            1 :             if (performanceInputMode_ == TransformerPerformanceInput::LossesMethod) {
    4548            1 :                 ShowWarningError(state, format("{}{}=\"{}\".", routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4549            1 :                 ShowContinueError(state, format("Specified {} = {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6)));
    4550            1 :                 ShowContinueError(state, format("Specified {} = {:.1R}", s_ipsc->cNumericFieldNames(2), ratedCapacity_));
    4551            2 :                 ShowContinueError(state, "Transformer load and no load losses cannot be calculated with 0.0 rated capacity.");
    4552            3 :                 ShowContinueError(state, "Simulation continues but transformer losses will be set to zero.");
    4553              :             }
    4554              :         }
    4555            1 :         ratedNL_ = s_ipsc->rNumericArgs(6);
    4556            1 :         ratedLL_ = s_ipsc->rNumericArgs(7);
    4557            1 :         ratedEfficiency_ = s_ipsc->rNumericArgs(8);
    4558            1 :         ratedPUL_ = s_ipsc->rNumericArgs(9);
    4559            1 :         ratedTemp_ = s_ipsc->rNumericArgs(10);
    4560            1 :         maxPUL_ = s_ipsc->rNumericArgs(11);
    4561              :         // Check the input for MaxPUL if the performance input method is EfficiencyMethod
    4562            1 :         if (performanceInputMode_ == TransformerPerformanceInput::EfficiencyMethod) {
    4563            0 :             if (s_ipsc->lNumericFieldBlanks(11)) {
    4564            0 :                 maxPUL_ = ratedPUL_;
    4565            0 :             } else if (maxPUL_ <= 0 || maxPUL_ > 1) {
    4566            0 :                 ShowSevereError(state, format("{}{}=\"{}\", invalid entry.", routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4567            0 :                 ShowContinueError(state, format("Invalid {}=[{:.3R}].", s_ipsc->cNumericFieldNames(11), s_ipsc->rNumericArgs(11)));
    4568            0 :                 ShowContinueError(state, "Entered value must be > 0 and <= 1.");
    4569            0 :                 errorsFound = true;
    4570              :             }
    4571              :         }
    4572              : 
    4573            1 :         BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(7));
    4574            1 :         if (bs != BooleanSwitch::Invalid) {
    4575            1 :             considerLosses_ = static_cast<bool>(bs);
    4576            0 :         } else if (usageMode_ == TransformerUse::PowerInFromGrid) {
    4577            0 :             ShowSevereInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
    4578            0 :             errorsFound = true;
    4579              :         }
    4580              : 
    4581            1 :         int numAlphaBeforeMeter = 7;
    4582            1 :         int numWiredMeters = numAlphas - numAlphaBeforeMeter;
    4583              : 
    4584            1 :         if (usageMode_ == TransformerUse::PowerInFromGrid) {
    4585              : 
    4586              :             // Provide warning if no meter is wired to a transformer used to get power from the grid
    4587            0 :             if (numWiredMeters <= 0) {
    4588            0 :                 ShowWarningError(state, format("{}ElectricLoadCenter:Transformer=\"{}\":", routineName, name_));
    4589            0 :                 ShowContinueError(state, "ISOLATED Transformer: No meter wired to a transformer used to input power from grid");
    4590              :             }
    4591              : 
    4592            0 :             wiredMeterNames_.resize(numWiredMeters, "");
    4593            0 :             wiredMeterPtrs_.resize(numWiredMeters, 0);
    4594            0 :             specialMeter_.resize(numWiredMeters, false);
    4595              : 
    4596              :             // Meter check deferred because they may have not been "loaded" yet,
    4597            0 :             for (int loopCount = 0; loopCount < numWiredMeters; ++loopCount) {
    4598            0 :                 wiredMeterNames_[loopCount] = Util::makeUPPER(s_ipsc->cAlphaArgs(loopCount + numAlphaBeforeMeter + 1));
    4599              :                 // Assign SpecialMeter as TRUE if the meter name is Electricity:Facility or Electricity:HVAC
    4600            0 :                 if (Util::SameString(wiredMeterNames_[loopCount], "Electricity:Facility") ||
    4601            0 :                     Util::SameString(wiredMeterNames_[loopCount], "Electricity:HVAC")) {
    4602            0 :                     specialMeter_[loopCount] = true;
    4603              :                 } else {
    4604            0 :                     specialMeter_[loopCount] = false;
    4605              :                 }
    4606              :             }
    4607              :         }
    4608            2 :         SetupOutputVariable(state,
    4609              :                             "Transformer Efficiency",
    4610              :                             Constant::Units::None,
    4611            1 :                             efficiency_,
    4612              :                             OutputProcessor::TimeStepType::System,
    4613              :                             OutputProcessor::StoreType::Average,
    4614            1 :                             name_);
    4615            2 :         SetupOutputVariable(state,
    4616              :                             "Transformer Input Electricity Rate",
    4617              :                             Constant::Units::W,
    4618            1 :                             powerIn_,
    4619              :                             OutputProcessor::TimeStepType::System,
    4620              :                             OutputProcessor::StoreType::Average,
    4621            1 :                             name_);
    4622            2 :         SetupOutputVariable(state,
    4623              :                             "Transformer Input Electricity Energy",
    4624              :                             Constant::Units::J,
    4625            1 :                             energyIn_,
    4626              :                             OutputProcessor::TimeStepType::System,
    4627              :                             OutputProcessor::StoreType::Sum,
    4628            1 :                             name_);
    4629            2 :         SetupOutputVariable(state,
    4630              :                             "Transformer Output Electricity Rate",
    4631              :                             Constant::Units::W,
    4632            1 :                             powerOut_,
    4633              :                             OutputProcessor::TimeStepType::System,
    4634              :                             OutputProcessor::StoreType::Average,
    4635            1 :                             name_);
    4636            2 :         SetupOutputVariable(state,
    4637              :                             "Transformer Output Electricity Energy",
    4638              :                             Constant::Units::J,
    4639            1 :                             energyOut_,
    4640              :                             OutputProcessor::TimeStepType::System,
    4641              :                             OutputProcessor::StoreType::Sum,
    4642            1 :                             name_);
    4643            2 :         SetupOutputVariable(state,
    4644              :                             "Transformer No Load Loss Rate",
    4645              :                             Constant::Units::W,
    4646            1 :                             noLoadLossRate_,
    4647              :                             OutputProcessor::TimeStepType::System,
    4648              :                             OutputProcessor::StoreType::Average,
    4649            1 :                             name_);
    4650            2 :         SetupOutputVariable(state,
    4651              :                             "Transformer No Load Loss Energy",
    4652              :                             Constant::Units::J,
    4653            1 :                             noLoadLossEnergy_,
    4654              :                             OutputProcessor::TimeStepType::System,
    4655              :                             OutputProcessor::StoreType::Sum,
    4656            1 :                             name_);
    4657            2 :         SetupOutputVariable(state,
    4658              :                             "Transformer Load Loss Rate",
    4659              :                             Constant::Units::W,
    4660            1 :                             loadLossRate_,
    4661              :                             OutputProcessor::TimeStepType::System,
    4662              :                             OutputProcessor::StoreType::Average,
    4663            1 :                             name_);
    4664            2 :         SetupOutputVariable(state,
    4665              :                             "Transformer Load Loss Energy",
    4666              :                             Constant::Units::J,
    4667            1 :                             loadLossEnergy_,
    4668              :                             OutputProcessor::TimeStepType::System,
    4669              :                             OutputProcessor::StoreType::Sum,
    4670            1 :                             name_);
    4671            2 :         SetupOutputVariable(state,
    4672              :                             "Transformer Thermal Loss Rate",
    4673              :                             Constant::Units::W,
    4674            1 :                             thermalLossRate_,
    4675              :                             OutputProcessor::TimeStepType::System,
    4676              :                             OutputProcessor::StoreType::Average,
    4677            1 :                             name_);
    4678            2 :         SetupOutputVariable(state,
    4679              :                             "Transformer Thermal Loss Energy",
    4680              :                             Constant::Units::J,
    4681            1 :                             thermalLossEnergy_,
    4682              :                             OutputProcessor::TimeStepType::System,
    4683              :                             OutputProcessor::StoreType::Sum,
    4684            1 :                             name_);
    4685            1 :         if (usageMode_ == TransformerUse::PowerInFromGrid) { // power losses metered as an end use exterior equipment
    4686            0 :             SetupOutputVariable(state,
    4687              :                                 "Transformer Distribution Electricity Loss Energy",
    4688              :                                 Constant::Units::J,
    4689            0 :                                 elecUseMeteredUtilityLosses_,
    4690              :                                 OutputProcessor::TimeStepType::System,
    4691              :                                 OutputProcessor::StoreType::Sum,
    4692            0 :                                 name_,
    4693              :                                 Constant::eResource::Electricity,
    4694              :                                 OutputProcessor::Group::HVAC, // Is this correct?
    4695              :                                 OutputProcessor::EndUseCat::ExteriorEquipment,
    4696              :                                 "Transformer");
    4697              :         }
    4698            1 :         if (usageMode_ == TransformerUse::PowerOutFromBldgToGrid) {
    4699            2 :             SetupOutputVariable(state,
    4700              :                                 "Transformer Cogeneration Electricity Loss Energy",
    4701              :                                 Constant::Units::J,
    4702            1 :                                 powerConversionMeteredLosses_,
    4703              :                                 OutputProcessor::TimeStepType::System,
    4704              :                                 OutputProcessor::StoreType::Sum,
    4705            1 :                                 name_,
    4706              :                                 Constant::eResource::ElectricityProduced,
    4707              :                                 OutputProcessor::Group::HVAC, // Is this correct?
    4708              :                                 OutputProcessor::EndUseCat::PowerConversion);
    4709              :         }
    4710            1 :         if (usageMode_ == TransformerUse::PowerBetweenLoadCenterAndBldg) {
    4711            0 :             SetupOutputVariable(state,
    4712              :                                 "Transformer Conversion Electricity Loss Energy",
    4713              :                                 Constant::Units::J,
    4714            0 :                                 powerConversionMeteredLosses_,
    4715              :                                 OutputProcessor::TimeStepType::System,
    4716              :                                 OutputProcessor::StoreType::Sum,
    4717            0 :                                 name_,
    4718              :                                 Constant::eResource::ElectricityProduced,
    4719              :                                 OutputProcessor::Group::HVAC, // Is this correct?
    4720              :                                 OutputProcessor::EndUseCat::PowerConversion);
    4721              :         }
    4722              : 
    4723            1 :         if (zoneNum_ > 0) {
    4724            0 :             SetupZoneInternalGain(
    4725              :                 state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterTransformer, &qdotConvZone_, nullptr, &qdotRadZone_);
    4726              :         }
    4727              : 
    4728              :     } else {
    4729            0 :         ShowSevereError(state, format("{} did not find transformer name = {}", routineName, objectName));
    4730            0 :         errorsFound = true;
    4731              :     }
    4732              : 
    4733            1 :     if (errorsFound) {
    4734            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    4735              :     }
    4736            1 : }
    4737              : 
    4738            1 : Real64 ElectricTransformer::getLossRateForOutputPower(EnergyPlusData &state, Real64 const powerOutOfTransformer)
    4739              : {
    4740            1 :     manageTransformers(state, powerOutOfTransformer);
    4741            1 :     return totalLossRate_;
    4742              : }
    4743              : 
    4744            0 : Real64 ElectricTransformer::getLossRateForInputPower(EnergyPlusData &state, Real64 const powerIntoTransformer)
    4745              : {
    4746            0 :     manageTransformers(state, powerIntoTransformer);
    4747            0 :     return totalLossRate_;
    4748              : }
    4749              : 
    4750            1 : void ElectricTransformer::manageTransformers(EnergyPlusData &state, Real64 const surplusPowerOutFromLoadCenters)
    4751              : {
    4752            1 :     Real64 constexpr ambTempRef = 20.0; // reference ambient temperature (C)
    4753            1 :     if (myOneTimeFlag_) {
    4754              :         // calculate rated no load losses and rated load losses if the performance input method is based on
    4755              :         // nominal efficiency. This calculation is done only once
    4756              : 
    4757            1 :         if (performanceInputMode_ == TransformerPerformanceInput::EfficiencyMethod) {
    4758              : 
    4759            0 :             Real64 resRef = factorTempCoeff_ + tempRise_ + ambTempRef;
    4760            0 :             Real64 resSpecified = factorTempCoeff_ + ratedTemp_;
    4761            0 :             Real64 resRatio = resSpecified / resRef;
    4762            0 :             Real64 factorTempCorr = (1.0 - eddyFrac_) * resRatio + eddyFrac_ * (1.0 / resRatio);
    4763            0 :             Real64 numerator = ratedCapacity_ * ratedPUL_ * (1.0 - ratedEfficiency_);
    4764            0 :             Real64 denominator = ratedEfficiency_ * (1.0 + pow_2(ratedPUL_ / maxPUL_));
    4765              : 
    4766            0 :             ratedNL_ = numerator / denominator;
    4767            0 :             ratedLL_ = ratedNL_ / (factorTempCorr * pow_2(maxPUL_));
    4768              :         }
    4769            1 :         myOneTimeFlag_ = false;
    4770              :     }
    4771              : 
    4772            1 :     Real64 elecLoad = 0.0;     // transformer load which may be power in or out depending on the usage mode
    4773            1 :     Real64 pastElecLoad = 0.0; // transformer load at the previous timestep
    4774            1 :     switch (usageMode_) {
    4775            0 :     case TransformerUse::PowerInFromGrid: {
    4776            0 :         for (std::size_t meterNum = 0; meterNum < wiredMeterPtrs_.size(); ++meterNum) {
    4777              : 
    4778            0 :             if (state.dataGlobal->MetersHaveBeenInitialized) {
    4779              : 
    4780            0 :                 elecLoad +=
    4781            0 :                     GetInstantMeterValue(state, wiredMeterPtrs_[meterNum], OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec +
    4782            0 :                     GetInstantMeterValue(state, wiredMeterPtrs_[meterNum], OutputProcessor::TimeStepType::System) /
    4783            0 :                         (state.dataHVACGlobal->TimeStepSysSec);
    4784              :                 // PastElecLoad store the metered value in the previous time step. This value will be used to check whether
    4785              :                 // a transformer is overloaded or not.
    4786            0 :                 pastElecLoad += GetCurrentMeterValue(state, wiredMeterPtrs_[meterNum]) / state.dataGlobal->TimeStepZoneSec;
    4787              :             } else {
    4788            0 :                 elecLoad = 0.0;
    4789            0 :                 pastElecLoad = 0.0;
    4790              :             }
    4791              : 
    4792              :             // Because transformer loss has been accounted for by Electricity:Facility and Electricity:HVAC, the transformer
    4793              :             // loss needs to be deducted from the metered value. Otherwise, double counting (circular relationship) occurs.
    4794            0 :             if (specialMeter_[meterNum]) {
    4795            0 :                 elecLoad = elecLoad - loadLossRate_ - noLoadLossRate_;
    4796              : 
    4797            0 :                 if (elecLoad < 0) {
    4798            0 :                     elecLoad = 0.0; // Essential check.
    4799              :                 }
    4800              :             }
    4801              :         }
    4802              : 
    4803            0 :         powerOut_ = elecLoad; // the metered value is transformer's output in PowerInFromGrid mode
    4804            0 :         break;
    4805              :     }
    4806            1 :     case TransformerUse::PowerOutFromBldgToGrid: {
    4807            1 :         powerIn_ = surplusPowerOutFromLoadCenters;
    4808            1 :         elecLoad = surplusPowerOutFromLoadCenters; // TODO this is input but should be output with the losses, but we don't have them yet.
    4809            1 :         break;
    4810              :     }
    4811            0 :     case TransformerUse::PowerBetweenLoadCenterAndBldg: {
    4812              :         // TODO, new configuration for transformer, really part of the specific load center and connects it to the main building bus
    4813            0 :         powerIn_ = surplusPowerOutFromLoadCenters;
    4814            0 :         elecLoad = surplusPowerOutFromLoadCenters;
    4815            0 :         break;
    4816              :     }
    4817            0 :     case TransformerUse::Invalid: {
    4818              :         // do nothing
    4819            0 :         break;
    4820              :     }
    4821            0 :     default:
    4822            0 :         assert(false);
    4823              :     } // switch usage mode
    4824              : 
    4825              :     // check availability schedule
    4826            1 :     if (ratedCapacity_ > 0.0 && availSched_->getCurrentVal() > 0.0) {
    4827              : 
    4828            0 :         Real64 pUL = elecLoad / ratedCapacity_;
    4829              : 
    4830            0 :         if (pUL > 1.0) {
    4831            0 :             pUL = 1.0;
    4832              :         }
    4833              : 
    4834              :         // Originally, PUL was used to check whether a transformer is overloaded (PUL > 1.0 or not). However, it was
    4835              :         // found that ElecLoad obtained from GetInstantMeterVlaue() might refer to intermideiate values before
    4836              :         // convergence. The intermediate values may issue false warning. This the reason why PastElecLoad obtained
    4837              :         // by GetCurrentMeterValue() is used here to check overload issue.
    4838            0 :         if ((pastElecLoad / ratedCapacity_) > 1.0) {
    4839            0 :             if (overloadErrorIndex_ == 0) {
    4840            0 :                 ShowSevereError(state, "Transformer Overloaded");
    4841            0 :                 ShowContinueError(state, format("Entered in ElectricLoadCenter:Transformer ={}", name_));
    4842              :             }
    4843            0 :             ShowRecurringSevereErrorAtEnd(state, "Transformer Overloaded: Entered in ElectricLoadCenter:Transformer =" + name_, overloadErrorIndex_);
    4844              :         }
    4845              : 
    4846            0 :         Real64 tempChange = std::pow(pUL, 1.6) * tempRise_;
    4847            0 :         Real64 ambTemp = 20.0;
    4848            0 :         if (heatLossesDestination_ == ThermalLossDestination::ZoneGains) {
    4849              : 
    4850            0 :             ambTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum_).MAT;
    4851              :         } else {
    4852            0 :             ambTemp = 20.0;
    4853              :         }
    4854              : 
    4855            0 :         Real64 resRef = factorTempCoeff_ + tempRise_ + ambTempRef;
    4856            0 :         Real64 resSpecified = factorTempCoeff_ + tempChange + ambTemp;
    4857            0 :         Real64 resRatio = resSpecified / resRef;
    4858            0 :         Real64 factorTempCorr = (1.0 - eddyFrac_) * resRatio + eddyFrac_ * (1.0 / resRatio);
    4859              : 
    4860            0 :         loadLossRate_ = ratedLL_ * pow_2(pUL) * factorTempCorr;
    4861            0 :         noLoadLossRate_ = ratedNL_;
    4862              :     } else { // Transformer is not available.
    4863            1 :         loadLossRate_ = 0.0;
    4864            1 :         noLoadLossRate_ = 0.0;
    4865              :     }
    4866              : 
    4867            1 :     totalLossRate_ = loadLossRate_ + noLoadLossRate_;
    4868              : 
    4869            1 :     switch (usageMode_) {
    4870            0 :     case TransformerUse::PowerInFromGrid: {
    4871            0 :         powerIn_ = elecLoad + totalLossRate_;
    4872              : 
    4873              :         // Transformer losses are wired to the meter via the variable "%ElecUseUtility" only if transformer losses
    4874              :         // are considered in utility cost. If transformer losses are not considered in utility cost, 0 is assigned
    4875              :         // to the variable "%ElecUseUtility".
    4876            0 :         if (considerLosses_) {
    4877            0 :             elecUseMeteredUtilityLosses_ = totalLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4878              :         } else {
    4879            0 :             elecUseMeteredUtilityLosses_ = 0.0;
    4880              :         }
    4881              : 
    4882              :         // Transformer has two modes.If it works in one mode, the variable for meter output in the other mode
    4883              :         // is assigned 0
    4884              :         // unused totalLossEnergy_ = totalLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4885              : 
    4886            0 :         break;
    4887              :     }
    4888              : 
    4889            1 :     case TransformerUse::PowerOutFromBldgToGrid:
    4890              :     case TransformerUse::PowerBetweenLoadCenterAndBldg: {
    4891            1 :         powerOut_ = elecLoad - totalLossRate_;
    4892              : 
    4893            1 :         if (powerOut_ < 0) {
    4894            0 :             powerOut_ = 0.0;
    4895              :         }
    4896              : 
    4897            1 :         powerConversionMeteredLosses_ = -1.0 * totalLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4898              : 
    4899              :         // Transformer has two modes.If it works in one mode, the variable for meter output in the other mode
    4900              :         // is assigned 0
    4901            1 :         elecUseMeteredUtilityLosses_ = 0.0;
    4902            1 :         break;
    4903              :     }
    4904              : 
    4905            0 :     case TransformerUse::Invalid: {
    4906              :         // do nothing
    4907            0 :         assert(false);
    4908              :     }
    4909            0 :     default:
    4910            0 :         assert(false);
    4911              :     } // switch
    4912              : 
    4913            1 :     if (powerIn_ <= 0) {
    4914            0 :         efficiency_ = 1.0; // Set to something reasonable to avoid a divide by zero error
    4915              :     } else {
    4916            1 :         efficiency_ = powerOut_ / powerIn_;
    4917              :     }
    4918            1 :     noLoadLossEnergy_ = noLoadLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4919            1 :     loadLossEnergy_ = loadLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4920              : 
    4921            1 :     energyIn_ = powerIn_ * state.dataHVACGlobal->TimeStepSysSec;
    4922            1 :     energyOut_ = powerOut_ * state.dataHVACGlobal->TimeStepSysSec;
    4923              : 
    4924              :     //   Thermal loss rate may not be equal to Total loss rate. This is the case when surplus power is less than the
    4925              :     //    calculated total loss rate for a cogeneration transformer. That is why "PowerIn - PowerOut" is used below.
    4926            1 :     thermalLossRate_ = powerIn_ - powerOut_;
    4927            1 :     thermalLossEnergy_ = thermalLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4928              : 
    4929            1 :     if (zoneNum_ > 0) { // set values for zone heat gains
    4930            0 :         qdotConvZone_ = (1.0 - zoneRadFrac_) * thermalLossRate_;
    4931            0 :         qdotRadZone_ = (zoneRadFrac_)*thermalLossRate_;
    4932              :     }
    4933            1 : }
    4934              : 
    4935            0 : void ElectricTransformer::setupMeterIndices(EnergyPlusData &state)
    4936              : {
    4937            0 :     if (usageMode_ == TransformerUse::PowerInFromGrid) {
    4938            0 :         for (std::size_t meterNum = 0; meterNum < wiredMeterNames_.size(); ++meterNum) {
    4939              : 
    4940            0 :             wiredMeterPtrs_[meterNum] = GetMeterIndex(state, wiredMeterNames_[meterNum]);
    4941              : 
    4942              :             // Check whether the meter is an electricity meter
    4943              :             // Index function is used here because some resource types are not Electricity but strings containing
    4944              :             // Electricity such as ElectricityPurchased and ElectricityProduced.
    4945              :             // It is not proper to have this check in GetInput routine because the meter index may have not been defined
    4946            0 :             auto *meter = state.dataOutputProcessor->meters[wiredMeterPtrs_[meterNum]];
    4947            0 :             if (meter->resource != Constant::eResource::Electricity && meter->resource != Constant::eResource::ElectricityPurchased &&
    4948            0 :                 meter->resource != Constant::eResource::ElectricitySurplusSold && meter->resource != Constant::eResource::ElectricityProduced &&
    4949            0 :                 meter->resource != Constant::eResource::ElectricityNet) {
    4950            0 :                 ShowFatalError(state, format("Non-electricity meter used for {}", name_));
    4951              :             }
    4952              :         }
    4953              :     }
    4954            0 : }
    4955              : 
    4956            0 : void ElectricTransformer::reinitAtBeginEnvironment()
    4957              : {
    4958            0 :     efficiency_ = 0.0;
    4959            0 :     powerIn_ = 0.0;
    4960            0 :     energyIn_ = 0.0;
    4961            0 :     powerOut_ = 0.0;
    4962            0 :     energyOut_ = 0.0;
    4963            0 :     noLoadLossRate_ = 0.0;
    4964            0 :     noLoadLossEnergy_ = 0.0;
    4965            0 :     loadLossRate_ = 0.0;
    4966            0 :     loadLossEnergy_ = 0.0;
    4967            0 :     thermalLossRate_ = 0.0;
    4968            0 :     thermalLossEnergy_ = 0.0;
    4969            0 :     elecUseMeteredUtilityLosses_ = 0.0;
    4970            0 :     powerConversionMeteredLosses_ = 0.0;
    4971            0 :     qdotConvZone_ = 0.0;
    4972            0 :     qdotRadZone_ = 0.0;
    4973            0 : }
    4974              : 
    4975            0 : void ElectricTransformer::reinitZoneGainsAtBeginEnvironment()
    4976              : {
    4977            0 :     qdotConvZone_ = 0.0;
    4978            0 :     qdotRadZone_ = 0.0;
    4979            0 : }
    4980              : 
    4981            0 : std::string const &ElectricTransformer::name() const
    4982              : {
    4983            0 :     return name_;
    4984              : }
    4985              : 
    4986              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1