LCOV - code coverage report
Current view: top level - EnergyPlus - ElectricPowerServiceManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 76.0 % 2850 2166
Test Date: 2025-06-02 07:23:51 Functions: 86.7 % 75 65

            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          801 : void createFacilityElectricPowerServiceObject(const EnergyPlusData &state)
      96              : {
      97          801 :     state.dataElectPwrSvcMgr->facilityElectricServiceObj = std::make_unique<ElectricPowerServiceManager>();
      98          801 : }
      99              : 
     100      2828408 : 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      2828408 :     if (state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag && state.dataGlobal->BeginEnvrnFlag) {
     104         6496 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->reinitZoneGainsAtBeginEnvironment();
     105         6496 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag = false;
     106              :     }
     107      2828408 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     108      2821912 :         state.dataElectPwrSvcMgr->facilityElectricServiceObj->newEnvironmentInternalGainsFlag = true;
     109              :     }
     110      2828408 : }
     111              : 
     112     13483490 : 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     13483490 :     if (getInputFlag_) {
     120          801 :         getPowerManagerInput(state);
     121          801 :         getInputFlag_ = false;
     122              :     }
     123              : 
     124     13483490 :     if (state.dataGlobal->MetersHaveBeenInitialized && setupMeterIndexFlag_) {
     125          800 :         setupMeterIndices(state);
     126          800 :         setupMeterIndexFlag_ = false;
     127              :     }
     128              : 
     129     13483490 :     if (state.dataGlobal->BeginEnvrnFlag && newEnvironmentFlag_) {
     130         6496 :         reinitAtBeginEnvironment();
     131         6496 :         newEnvironmentFlag_ = false;
     132              :     }
     133     13483490 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     134     13426430 :         newEnvironmentFlag_ = true;
     135              :     }
     136              : 
     137              :     // retrieve data from meters for demand and production
     138     13483490 :     totalBldgElecDemand_ =
     139     13483490 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec;
     140     13483490 :     totalHVACElecDemand_ =
     141     13483490 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     142     13483490 :     totalElectricDemand_ = totalBldgElecDemand_ + totalHVACElecDemand_;
     143     13483490 :     elecProducedPVRate_ =
     144     13483490 :         GetInstantMeterValue(state, elecProducedPVMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     145     13483490 :     elecProducedWTRate_ =
     146     13483490 :         GetInstantMeterValue(state, elecProducedWTMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     147     13483490 :     elecProducedStorageRate_ =
     148     13483490 :         GetInstantMeterValue(state, elecProducedStorageMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     149     13483490 :     elecProducedCoGenRate_ =
     150     13483490 :         GetInstantMeterValue(state, elecProducedCoGenMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     151     13483490 :     elecProducedPowerConversionRate_ = GetInstantMeterValue(state, elecProducedPowerConversionMeterIndex_, OutputProcessor::TimeStepType::System) /
     152     13483490 :                                        (state.dataHVACGlobal->TimeStepSysSec);
     153              : 
     154     13483490 :     wholeBldgRemainingLoad_ = totalElectricDemand_;
     155              : 
     156     13483490 :     if (UpdateMetersOnly) { // just update record keeping, don't resimulate load centers
     157      3588830 :         if (facilityPowerInTransformerPresent_) {
     158        34411 :             facilityPowerInTransformerObj_->manageTransformers(state, 0.0);
     159              :         }
     160              : 
     161      3588830 :         updateWholeBuildingRecords(state);
     162      3588830 :         return;
     163              :     }
     164              : 
     165     19231887 :     for (auto &e : elecLoadCenterObjs) {
     166      9337227 :         e->manageElecLoadCenter(state, firstHVACIteration, wholeBldgRemainingLoad_);
     167      9894660 :     }
     168              : 
     169      9894660 :     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      9894660 :     if (facilityPowerInTransformerPresent_) {
     174       103709 :         facilityPowerInTransformerObj_->manageTransformers(state, 0.0);
     175              :     }
     176              : 
     177      9894660 :     updateWholeBuildingRecords(state);
     178      9894660 :     if (powerOutTransformerObj_ != nullptr) {
     179         8508 :         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      9894660 :     if (firstHVACIteration) {
     187      6753598 :         SimElecCircuits = true;
     188              :     } else {
     189      3141062 :         SimElecCircuits = false;
     190              :     }
     191              : }
     192              : 
     193         6496 : void ElectricPowerServiceManager::reinitZoneGainsAtBeginEnvironment()
     194              : {
     195         6496 :     if (facilityPowerInTransformerPresent_) {
     196          117 :         facilityPowerInTransformerObj_->reinitZoneGainsAtBeginEnvironment();
     197              :     }
     198         6496 :     if (powerOutTransformerObj_ != nullptr) {
     199            5 :         powerOutTransformerObj_->reinitZoneGainsAtBeginEnvironment();
     200              :     }
     201         6496 :     if (numLoadCenters_ > 0) {
     202        10708 :         for (auto &e : elecLoadCenterObjs) {
     203         5362 :             e->reinitZoneGainsAtBeginEnvironment();
     204         5346 :         }
     205              :     }
     206         6496 : }
     207              : 
     208          801 : void ElectricPowerServiceManager::getPowerManagerInput(EnergyPlusData &state)
     209              : {
     210              :     static constexpr std::string_view routineName = "ElectricPowerServiceManager::getPowerManagerInput ";
     211              : 
     212          801 :     auto &s_ipsc = state.dataIPShortCut;
     213              : 
     214          801 :     numLoadCenters_ = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Distribution");
     215              : 
     216          801 :     if (numLoadCenters_ > 0) {
     217           48 :         for (int iLoadCenterNum = 1; iLoadCenterNum <= numLoadCenters_; ++iLoadCenterNum) {
     218              :             // call Electric Power Load Center constructor, in place
     219           25 :             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          778 :         bool errorsFound(false);
     224          778 :         int numGenLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Generators");
     225          778 :         if (numGenLists > 0) {
     226            0 :             ShowSevereError(state, "ElectricLoadCenter:Generators input object requires an ElectricLoadCenterDistribution input object.");
     227            0 :             errorsFound = true;
     228              :         }
     229          778 :         int numInverters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:Simple");
     230          778 :         numInverters += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:FunctionOfPower");
     231          778 :         numInverters += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Inverter:LookUpTable");
     232          778 :         if (numInverters > 0) {
     233            0 :             ShowSevereError(state, "ElectricLoadCenter:Inverter:* input objects require an ElectricLoadCenter:Distribution input object.");
     234            0 :             errorsFound = true;
     235              :         }
     236          778 :         int numStorage = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Storage:Simple");
     237          778 :         numStorage += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Storage:Battery");
     238          778 :         if (numStorage > 0) {
     239            0 :             ShowSevereError(state, "ElectricLoadCenter:Storage:* input objects require an ElectricLoadCenter:Distribution input object.");
     240            0 :             errorsFound = true;
     241              :         }
     242          778 :         int numGenerators = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:InternalCombustionEngine");
     243          778 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:CombustionTurbine");
     244          778 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:MicroCHP");
     245          778 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:FuelCell");
     246          778 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:Photovoltaic");
     247          778 :         numGenerators += state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:WindTurbine");
     248          778 :         if (numGenerators > 0) {
     249            0 :             ShowSevereError(state, "Electric generator input objects require an ElectricLoadCenter:Distribution input object.");
     250            0 :             errorsFound = true;
     251              :         }
     252              : 
     253          778 :         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          778 :         int anyElectricityPresent = GetMeterIndex(state, "ELECTRICITY:FACILITY");
     260          778 :         int anyPlantLoadProfilePresent = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "LoadProfile:Plant");
     261          778 :         if (anyElectricityPresent > -1 || anyPlantLoadProfilePresent > 0) {
     262          710 :             elecLoadCenterObjs.emplace_back(new ElectPowerLoadCenter(state, 0));
     263          710 :             numLoadCenters_ = 1;
     264              :         }
     265              :     }
     266              : 
     267              :     // see if there are any transformers of the type PowerInFromGrid
     268          801 :     numTransformers_ = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ElectricLoadCenter:Transformer");
     269              : 
     270          801 :     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           13 :         bool foundInFromGridTransformer = false;
     275              : 
     276           13 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
     277           26 :         for (int loopTransformer = 1; loopTransformer <= numTransformers_; ++loopTransformer) {
     278           26 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     279           13 :                                                                      s_ipsc->cCurrentModuleObject,
     280              :                                                                      loopTransformer,
     281           13 :                                                                      s_ipsc->cAlphaArgs,
     282              :                                                                      numAlphas,
     283           13 :                                                                      s_ipsc->rNumericArgs,
     284              :                                                                      numNums,
     285              :                                                                      iOStat,
     286           13 :                                                                      s_ipsc->lNumericFieldBlanks,
     287           13 :                                                                      s_ipsc->lAlphaFieldBlanks,
     288           13 :                                                                      s_ipsc->cAlphaFieldNames,
     289           13 :                                                                      s_ipsc->cNumericFieldNames);
     290              : 
     291           13 :             if (Util::SameString(s_ipsc->cAlphaArgs(3), "PowerInFromGrid")) {
     292           12 :                 if (!foundInFromGridTransformer) {
     293           12 :                     foundInFromGridTransformer = true;
     294           12 :                     facilityPowerInTransformerName_ = s_ipsc->cAlphaArgs(1);
     295           12 :                     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            1 :             } else if (Util::SameString(s_ipsc->cAlphaArgs(3), "PowerOutToGrid")) {
     305            1 :                 if (powerOutTransformerObj_ == nullptr) {
     306            1 :                     ++numPowerOutTransformers_;
     307            1 :                     powerOutTransformerName_ = s_ipsc->cAlphaArgs(1);
     308            1 :                     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           13 :         if (foundInFromGridTransformer) {
     317              :             // call transformer constructor
     318           12 :             facilityPowerInTransformerObj_ = std::make_unique<ElectricTransformer>(state, facilityPowerInTransformerName_);
     319              :         }
     320              :     } // if transformers
     321              : 
     322          801 :     if (numLoadCenters_ > 0) {
     323         1466 :         SetupOutputVariable(state,
     324              :                             "Facility Total Purchased Electricity Rate",
     325              :                             Constant::Units::W,
     326          733 :                             electPurchRate_,
     327              :                             OutputProcessor::TimeStepType::System,
     328              :                             OutputProcessor::StoreType::Average,
     329          733 :                             name_);
     330         1466 :         SetupOutputVariable(state,
     331              :                             "Facility Total Purchased Electricity Energy",
     332              :                             Constant::Units::J,
     333          733 :                             electricityPurch_,
     334              :                             OutputProcessor::TimeStepType::System,
     335              :                             OutputProcessor::StoreType::Sum,
     336          733 :                             name_,
     337              :                             Constant::eResource::ElectricityPurchased,
     338              :                             OutputProcessor::Group::Plant,
     339              :                             OutputProcessor::EndUseCat::Cogeneration);
     340              : 
     341         1466 :         SetupOutputVariable(state,
     342              :                             "Facility Total Surplus Electricity Rate",
     343              :                             Constant::Units::W,
     344          733 :                             electSurplusRate_,
     345              :                             OutputProcessor::TimeStepType::System,
     346              :                             OutputProcessor::StoreType::Average,
     347          733 :                             name_);
     348         1466 :         SetupOutputVariable(state,
     349              :                             "Facility Total Surplus Electricity Energy",
     350              :                             Constant::Units::J,
     351          733 :                             electricitySurplus_,
     352              :                             OutputProcessor::TimeStepType::System,
     353              :                             OutputProcessor::StoreType::Sum,
     354          733 :                             name_,
     355              :                             Constant::eResource::ElectricitySurplusSold,
     356              :                             OutputProcessor::Group::Plant,
     357              :                             OutputProcessor::EndUseCat::Cogeneration);
     358              : 
     359         1466 :         SetupOutputVariable(state,
     360              :                             "Facility Net Purchased Electricity Rate",
     361              :                             Constant::Units::W,
     362          733 :                             electricityNetRate_,
     363              :                             OutputProcessor::TimeStepType::System,
     364              :                             OutputProcessor::StoreType::Average,
     365          733 :                             name_);
     366         1466 :         SetupOutputVariable(state,
     367              :                             "Facility Net Purchased Electricity Energy",
     368              :                             Constant::Units::J,
     369          733 :                             electricityNet_,
     370              :                             OutputProcessor::TimeStepType::System,
     371              :                             OutputProcessor::StoreType::Sum,
     372          733 :                             name_,
     373              :                             Constant::eResource::ElectricityNet,
     374              :                             OutputProcessor::Group::Plant,
     375              :                             OutputProcessor::EndUseCat::Cogeneration);
     376              : 
     377         1466 :         SetupOutputVariable(state,
     378              :                             "Facility Total Building Electricity Demand Rate",
     379              :                             Constant::Units::W,
     380          733 :                             totalBldgElecDemand_,
     381              :                             OutputProcessor::TimeStepType::System,
     382              :                             OutputProcessor::StoreType::Average,
     383          733 :                             name_);
     384         1466 :         SetupOutputVariable(state,
     385              :                             "Facility Total HVAC Electricity Demand Rate",
     386              :                             Constant::Units::W,
     387          733 :                             totalHVACElecDemand_,
     388              :                             OutputProcessor::TimeStepType::System,
     389              :                             OutputProcessor::StoreType::Average,
     390          733 :                             name_);
     391         1466 :         SetupOutputVariable(state,
     392              :                             "Facility Total Electricity Demand Rate",
     393              :                             Constant::Units::W,
     394          733 :                             totalElectricDemand_,
     395              :                             OutputProcessor::TimeStepType::System,
     396              :                             OutputProcessor::StoreType::Average,
     397          733 :                             name_);
     398              : 
     399         1466 :         SetupOutputVariable(state,
     400              :                             "Facility Total Produced Electricity Rate",
     401              :                             Constant::Units::W,
     402          733 :                             electProdRate_,
     403              :                             OutputProcessor::TimeStepType::System,
     404              :                             OutputProcessor::StoreType::Average,
     405          733 :                             name_);
     406         1466 :         SetupOutputVariable(state,
     407              :                             "Facility Total Produced Electricity Energy",
     408              :                             Constant::Units::J,
     409          733 :                             electricityProd_,
     410              :                             OutputProcessor::TimeStepType::System,
     411              :                             OutputProcessor::StoreType::Sum,
     412          733 :                             name_);
     413              : 
     414          733 :         reportPVandWindCapacity(state);
     415              : 
     416          733 :         sumUpNumberOfStorageDevices();
     417              : 
     418          733 :         checkLoadCenters(state);
     419              :     }
     420          801 : }
     421              : 
     422          800 : void ElectricPowerServiceManager::setupMeterIndices(EnergyPlusData &state)
     423              : {
     424         1600 :     elecFacilityMeterIndex_ = GetMeterIndex(state, "ELECTRICITY:FACILITY");
     425         1600 :     elecProducedCoGenMeterIndex_ = GetMeterIndex(state, "COGENERATION:ELECTRICITYPRODUCED");
     426         1600 :     elecProducedPVMeterIndex_ = GetMeterIndex(state, "PHOTOVOLTAIC:ELECTRICITYPRODUCED");
     427         1600 :     elecProducedWTMeterIndex_ = GetMeterIndex(state, "WINDTURBINE:ELECTRICITYPRODUCED");
     428         1600 :     elecProducedStorageMeterIndex_ = GetMeterIndex(state, "ELECTRICSTORAGE:ELECTRICITYPRODUCED");
     429          800 :     elecProducedPowerConversionMeterIndex_ = GetMeterIndex(state, "POWERCONVERSION:ELECTRICITYPRODUCED");
     430              : 
     431          800 :     if (numLoadCenters_ > 0) {
     432         1466 :         for (auto &e : elecLoadCenterObjs) {
     433          734 :             e->setupLoadCenterMeterIndices(state);
     434          732 :         }
     435              :     }
     436          800 :     if (facilityPowerInTransformerPresent_) {
     437           12 :         facilityPowerInTransformerObj_->setupMeterIndices(state);
     438              :     }
     439          800 : }
     440              : 
     441         6496 : void ElectricPowerServiceManager::reinitAtBeginEnvironment()
     442              : {
     443         6496 :     wholeBldgRemainingLoad_ = 0.0;
     444         6496 :     electricityProd_ = 0.0;
     445         6496 :     electProdRate_ = 0.0;
     446         6496 :     electricityPurch_ = 0.0;
     447         6496 :     electPurchRate_ = 0.0;
     448         6496 :     electSurplusRate_ = 0.0;
     449         6496 :     electricitySurplus_ = 0.0;
     450         6496 :     electricityNetRate_ = 0.0;
     451         6496 :     electricityNet_ = 0.0;
     452         6496 :     totalBldgElecDemand_ = 0.0;
     453         6496 :     totalHVACElecDemand_ = 0.0;
     454         6496 :     totalElectricDemand_ = 0.0;
     455         6496 :     elecProducedPVRate_ = 0.0;
     456         6496 :     elecProducedWTRate_ = 0.0;
     457         6496 :     elecProducedStorageRate_ = 0.0;
     458         6496 :     elecProducedCoGenRate_ = 0.0;
     459              : 
     460         6496 :     if (numLoadCenters_ > 0) {
     461        12176 :         for (auto &e : elecLoadCenterObjs) {
     462         6097 :             e->reinitAtBeginEnvironment();
     463         6079 :         }
     464              :     }
     465         6496 :     if (facilityPowerInTransformerPresent_) {
     466          129 :         facilityPowerInTransformerObj_->reinitAtBeginEnvironment();
     467              :     }
     468         6496 :     if (powerOutTransformerObj_ != nullptr) {
     469            6 :         powerOutTransformerObj_->reinitAtBeginEnvironment();
     470              :     }
     471         6496 : }
     472              : 
     473          800 : void ElectricPowerServiceManager::verifyCustomMetersElecPowerMgr(EnergyPlusData &state)
     474              : {
     475         1534 :     for (std::size_t loop = 0; loop < elecLoadCenterObjs.size(); ++loop) {
     476          734 :         elecLoadCenterObjs[loop]->setupLoadCenterMeterIndices(state);
     477              :     }
     478          800 : }
     479              : 
     480     23378150 : void ElectricPowerServiceManager::updateWholeBuildingRecords(EnergyPlusData &state)
     481              : {
     482              : 
     483              :     // main panel balancing.
     484     23378150 :     totalBldgElecDemand_ =
     485     23378150 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec;
     486     23378150 :     totalHVACElecDemand_ =
     487     23378150 :         GetInstantMeterValue(state, elecFacilityMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     488     23378150 :     totalElectricDemand_ = totalBldgElecDemand_ + totalHVACElecDemand_;
     489     23378150 :     elecProducedPVRate_ =
     490     23378150 :         GetInstantMeterValue(state, elecProducedPVMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     491     23378150 :     elecProducedWTRate_ =
     492     23378150 :         GetInstantMeterValue(state, elecProducedWTMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     493     23378150 :     elecProducedStorageRate_ =
     494     23378150 :         GetInstantMeterValue(state, elecProducedStorageMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     495     23378150 :     elecProducedCoGenRate_ =
     496     23378150 :         GetInstantMeterValue(state, elecProducedCoGenMeterIndex_, OutputProcessor::TimeStepType::System) / (state.dataHVACGlobal->TimeStepSysSec);
     497     23378150 :     elecProducedPowerConversionRate_ = GetInstantMeterValue(state, elecProducedPowerConversionMeterIndex_, OutputProcessor::TimeStepType::System) /
     498     23378150 :                                        (state.dataHVACGlobal->TimeStepSysSec);
     499              : 
     500     23378150 :     electProdRate_ = elecProducedCoGenRate_ + elecProducedPVRate_ + elecProducedWTRate_ + elecProducedStorageRate_ + elecProducedPowerConversionRate_;
     501     23378150 :     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     23378150 :     electPurchRate_ = totalElectricDemand_ - electProdRate_;
     505              :     // Check this value against a tolerance to aid in reporting.
     506     23378150 :     if (std::abs(electPurchRate_) < 0.0001) {
     507      4953343 :         electPurchRate_ = 0.0;
     508              :     }
     509     23378150 :     if (electPurchRate_ < 0.0) {
     510        99275 :         electPurchRate_ = 0.0; // don't want negative purchased...
     511              :     }
     512              : 
     513              :     // Report the Total Electric Energy Purchased [J]
     514     23378150 :     electricityPurch_ = electPurchRate_ * state.dataHVACGlobal->TimeStepSysSec;
     515              : 
     516              :     // report the total electric surplus....
     517     23378150 :     electSurplusRate_ = electProdRate_ - totalElectricDemand_;
     518     23378150 :     if (std::abs(electSurplusRate_) < 0.0001) {
     519      4953343 :         electSurplusRate_ = 0.0;
     520              :     }
     521     23378150 :     if (electSurplusRate_ < 0.0) {
     522     18325532 :         electSurplusRate_ = 0.0; // don't want negative surplus
     523              :     }
     524              : 
     525     23378150 :     electricitySurplus_ = electSurplusRate_ * state.dataHVACGlobal->TimeStepSysSec;
     526              : 
     527              :     // report the net electricity , + is purchased, - is surplus
     528     23378150 :     electricityNetRate_ = totalElectricDemand_ - electProdRate_;
     529              : 
     530     23378150 :     electricityNet_ = electricityNetRate_ * state.dataHVACGlobal->TimeStepSysSec;
     531     23378150 : }
     532              : 
     533          733 : void ElectricPowerServiceManager::reportPVandWindCapacity(EnergyPlusData &state)
     534              : {
     535              :     // LEED report
     536          733 :     pvTotalCapacity_ = 0.0;
     537          733 :     windTotalCapacity_ = 0.0;
     538         1468 :     for (auto const &lc : elecLoadCenterObjs) {
     539          735 :         if (lc->numGenerators > 0) {
     540          106 :             for (auto const &g : lc->elecGenCntrlObj) {
     541           85 :                 if (g->generatorType == GeneratorType::PV) {
     542           58 :                     pvTotalCapacity_ += g->maxPowerOut;
     543              :                 }
     544           85 :                 if (g->generatorType == GeneratorType::WindTurbine) {
     545            3 :                     windTotalCapacity_ += g->maxPowerOut;
     546              :                 }
     547           21 :             }
     548              :         }
     549          733 :     }
     550              :     // put in total capacity for PV and Wind for LEED report
     551          733 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedRenRatCap, "Photovoltaic", pvTotalCapacity_ / 1000, 2);
     552          733 :     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          733 : }
     558              : 
     559          733 : void ElectricPowerServiceManager::sumUpNumberOfStorageDevices()
     560              : {
     561          733 :     numElecStorageDevices = 0;
     562         1468 :     for (auto const &e : elecLoadCenterObjs) {
     563          735 :         if (e->storageObj != nullptr) {
     564            9 :             ++numElecStorageDevices;
     565              :         }
     566          733 :     }
     567          733 : }
     568              : 
     569          733 : 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          733 :     bool errorsFound = false;
     575              : 
     576              :     // first fill in a vector of names
     577          733 :     std::vector<std::string> storageNames;
     578          733 :     std::vector<std::string> genListNames;
     579          733 :     std::vector<std::string> inverterNames;
     580          733 :     std::vector<std::string> converterNames;
     581          733 :     std::vector<std::string> transformerNames;
     582         1468 :     for (auto &e : elecLoadCenterObjs) {
     583          735 :         if (e->storageObj != nullptr) {
     584            9 :             storageNames.emplace_back(e->storageObj->name());
     585              :         }
     586          735 :         if (!e->elecGenCntrlObj.empty()) {
     587           21 :             genListNames.emplace_back(e->generatorListName());
     588              :         }
     589          735 :         if (e->inverterObj != nullptr) {
     590           12 :             inverterNames.emplace_back(e->inverterObj->name());
     591              :         }
     592          735 :         if (e->converterObj != nullptr) {
     593            4 :             converterNames.emplace_back(e->converterObj->name());
     594              :         }
     595          735 :         if (e->transformerObj != nullptr) {
     596            0 :             transformerNames.emplace_back(e->transformerObj->name());
     597              :         }
     598          733 :     }
     599              : 
     600              :     // then check the vectors for duplicates.
     601          742 :     for (std::size_t i = 0; i < storageNames.size(); ++i) {
     602           18 :         for (std::size_t j = 0; j < storageNames.size(); ++j) {
     603            9 :             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            9 :         if (errorsFound) {
     614            0 :             break;
     615              :         }
     616              :     }
     617              : 
     618          754 :     for (std::size_t i = 0; i < genListNames.size(); ++i) {
     619           48 :         for (std::size_t j = 0; j < genListNames.size(); ++j) {
     620           27 :             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           21 :         if (errorsFound) {
     631            0 :             break;
     632              :         }
     633              :     }
     634              : 
     635          745 :     for (std::size_t i = 0; i < inverterNames.size(); ++i) {
     636           24 :         for (std::size_t j = 0; j < inverterNames.size(); ++j) {
     637           12 :             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           12 :         if (errorsFound) {
     648            0 :             break;
     649              :         }
     650              :     }
     651              : 
     652          737 :     for (std::size_t i = 0; i < converterNames.size(); ++i) {
     653            8 :         for (std::size_t j = 0; j < converterNames.size(); ++j) {
     654            4 :             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            4 :         if (errorsFound) {
     665            0 :             break;
     666              :         }
     667              :     }
     668              : 
     669          733 :     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          733 :     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          733 : }
     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          735 : ElectPowerLoadCenter::ElectPowerLoadCenter(EnergyPlusData &state, int const objectNum)
     695         1470 :     : numGenerators(0), bussType(ElectricBussType::Invalid), thermalProd(0.0), thermalProdRate(0.0), inverterPresent(false),
     696          735 :       subpanelFeedInRequest(0.0), subpanelFeedInRate(0.0), subpanelDrawRate(0.0), genElectricProd(0.0), genElectProdRate(0.0), storOpCVDrawRate(0.0),
     697          735 :       storOpCVFeedInRate(0.0), storOpCVChargeRate(0.0), storOpCVDischargeRate(0.0), storOpIsCharging(false), storOpIsDischarging(false),
     698         2205 :       genOperationScheme_(GeneratorOpScheme::Invalid), demandMeterPtr_(0), generatorsPresent_(false), myCoGenSetupFlag_(true), demandLimit_(0.0),
     699         2205 :       storagePresent_(false), transformerPresent_(false), totalPowerRequest_(0.0), totalThermalPowerRequest_(0.0),
     700         2205 :       storageScheme_(StorageOpScheme::Invalid), trackStorageOpMeterIndex_(0), converterPresent_(false), maxStorageSOCFraction_(1.0),
     701          735 :       minStorageSOCFraction_(0.0), designStorageChargePower_(0.0), designStorageChargePowerWasSet_(false), designStorageDischargePower_(0.0),
     702          735 :       designStorageDischargePowerWasSet_(false), facilityDemandTarget_(0.0), eMSOverridePelFromStorage_(false), // if true, EMS calling for override
     703          735 :       eMSValuePelFromStorage_(0.0),      // value EMS is directing to use, power from storage [W]
     704          735 :       eMSOverridePelIntoStorage_(false), // if true, EMS calling for override
     705          735 :       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          735 :     auto &s_ipsc = state.dataIPShortCut;
     714              : 
     715          735 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Distribution";
     716          735 :     bool errorsFound = false;
     717          735 :     if (objectNum > 0) {
     718           50 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     719           25 :                                                                  s_ipsc->cCurrentModuleObject,
     720              :                                                                  objectNum,
     721           25 :                                                                  s_ipsc->cAlphaArgs,
     722              :                                                                  numAlphas,
     723           25 :                                                                  s_ipsc->rNumericArgs,
     724              :                                                                  numNums,
     725              :                                                                  IOStat,
     726           25 :                                                                  s_ipsc->lNumericFieldBlanks,
     727           25 :                                                                  s_ipsc->lAlphaFieldBlanks,
     728           25 :                                                                  s_ipsc->cAlphaFieldNames,
     729           25 :                                                                  s_ipsc->cNumericFieldNames);
     730              : 
     731           25 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     732              : 
     733           25 :         name_ = s_ipsc->cAlphaArgs(1);
     734              :         // how to verify names are unique across objects? add to GlobalNames?
     735              : 
     736           25 :         if (!s_ipsc->lAlphaFieldBlanks(2)) {
     737           21 :             generatorListName_ = s_ipsc->cAlphaArgs(2);
     738              :             // check that
     739              : 
     740           21 :             int testIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Generators", generatorListName_);
     741           21 :             if (testIndex == 0) {
     742            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     743            0 :                 errorsFound = true;
     744              :             }
     745              :         }
     746              : 
     747           25 :         if (!s_ipsc->lAlphaFieldBlanks(3)) {
     748              :             // Load the Generator Control Operation Scheme
     749           21 :             genOperationScheme_ = static_cast<GeneratorOpScheme>(getEnumValue(generatorOpSchemeNamesUC, s_ipsc->cAlphaArgs(3)));
     750           21 :             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           25 :         demandLimit_ = s_ipsc->rNumericArgs(1);
     757              : 
     758           25 :         if (genOperationScheme_ == GeneratorOpScheme::TrackSchedule) {
     759            1 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
     760            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
     761            0 :                 errorsFound = true;
     762            1 :             } 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           25 :         demandMeterName_ = Util::makeUPPER(s_ipsc->cAlphaArgs(5));
     769              :         // meters may not be "loaded" yet, defered check to later subroutine
     770              : 
     771           25 :         if (s_ipsc->cAlphaArgs(6).empty()) {
     772            0 :             bussType = ElectricBussType::ACBuss;
     773           25 :         } 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           25 :         } else if (bussType == ElectricBussType::DCBussInverter) {
     778            3 :             inverterPresent = true;
     779           22 :         } else if (bussType == ElectricBussType::ACBussStorage) {
     780            0 :             storagePresent_ = true;
     781           22 :         } else if (bussType == ElectricBussType::DCBussInverterDCStorage || bussType == ElectricBussType::DCBussInverterACStorage) {
     782            9 :             inverterPresent = true;
     783            9 :             storagePresent_ = true;
     784              :         }
     785              : 
     786           25 :         if (inverterPresent) {
     787           12 :             if (!s_ipsc->lAlphaFieldBlanks(7)) {
     788           12 :                 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           25 :         if (storagePresent_) {
     796            9 :             if (!s_ipsc->lAlphaFieldBlanks(8)) {
     797            9 :                 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           25 :         if (!s_ipsc->lAlphaFieldBlanks(9)) {
     805              :             // process transformer
     806            0 :             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            0 :             transformerPresent_ = true;
     810              :         }
     811              : 
     812              :         // Begin new content for grid supply and more control over storage
     813              :         // user selected storage operation scheme
     814           25 :         if (s_ipsc->lAlphaFieldBlanks(10)) {
     815           19 :             storageScheme_ = StorageOpScheme::FacilityDemandStoreExcessOnSite;
     816            6 :         } 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           25 :         if (!s_ipsc->lAlphaFieldBlanks(11)) {
     823            0 :             trackSorageOpMeterName_ = s_ipsc->cAlphaArgs(11);
     824           25 :         } 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           25 :         if (!s_ipsc->lAlphaFieldBlanks(12)) {
     830            4 :             converterName_ = s_ipsc->cAlphaArgs(12);
     831            4 :             converterPresent_ = true;
     832           21 :         } 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           25 :         if (s_ipsc->lNumericFieldBlanks(2)) {
     838           19 :             maxStorageSOCFraction_ = 1.0;
     839              :         } else {
     840            6 :             maxStorageSOCFraction_ = s_ipsc->rNumericArgs(2);
     841              :         }
     842           25 :         if (s_ipsc->lNumericFieldBlanks(3)) {
     843           19 :             minStorageSOCFraction_ = 0.0;
     844              :         } else {
     845            6 :             minStorageSOCFraction_ = s_ipsc->rNumericArgs(3);
     846              :         }
     847           25 :         if (s_ipsc->lNumericFieldBlanks(4)) {
     848           21 :             designStorageChargePowerWasSet_ = false;
     849              :         } else {
     850            4 :             designStorageChargePowerWasSet_ = true;
     851            4 :             designStorageChargePower_ = s_ipsc->rNumericArgs(4);
     852              :         }
     853           25 :         if (s_ipsc->lNumericFieldBlanks(5)) {
     854           21 :             designStorageDischargePowerWasSet_ = false;
     855              :         } else {
     856            4 :             designStorageDischargePowerWasSet_ = true;
     857            4 :             designStorageDischargePower_ = s_ipsc->rNumericArgs(5);
     858              :         }
     859              : 
     860           25 :         if (!s_ipsc->lNumericFieldBlanks(6)) {
     861            1 :             facilityDemandTarget_ = s_ipsc->rNumericArgs(6);
     862           24 :         } 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           25 :         if (storageScheme_ == StorageOpScheme::ChargeDischargeSchedules) {
     868            3 :             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            3 :             } 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            3 :             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            3 :             } 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           25 :         if (storageScheme_ == StorageOpScheme::FacilityDemandLeveling) {
     886            1 :             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            1 :             } 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          710 :         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           25 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Generators";
     902           25 :     int genListObjectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, s_ipsc->cCurrentModuleObject, generatorListName_);
     903           25 :     if (genListObjectNum > 0) {
     904           42 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     905           21 :                                                                  s_ipsc->cCurrentModuleObject,
     906              :                                                                  genListObjectNum,
     907           21 :                                                                  s_ipsc->cAlphaArgs,
     908              :                                                                  numAlphas,
     909           21 :                                                                  s_ipsc->rNumericArgs,
     910              :                                                                  numNums,
     911              :                                                                  IOStat,
     912           21 :                                                                  s_ipsc->lNumericFieldBlanks,
     913           21 :                                                                  s_ipsc->lAlphaFieldBlanks,
     914           21 :                                                                  s_ipsc->cAlphaFieldNames,
     915           21 :                                                                  s_ipsc->cNumericFieldNames);
     916              : 
     917              :         // Calculate the number of generators in list
     918           21 :         numGenerators = numNums / 2; // note IDD needs Min Fields = 6
     919           21 :         if (mod((numAlphas - 1 + numNums), 5) != 0) {
     920            6 :             ++numGenerators;
     921              :         }
     922           21 :         int alphaCount = 2;
     923          106 :         for (int genCount = 1; genCount <= numGenerators; ++genCount) {
     924              :             // call constructor in place
     925           85 :             generatorsPresent_ = true;
     926           85 :             elecGenCntrlObj.emplace_back(new GeneratorController(state,
     927           85 :                                                                  s_ipsc->cAlphaArgs(alphaCount),
     928           85 :                                                                  s_ipsc->cAlphaArgs(alphaCount + 1),
     929           85 :                                                                  s_ipsc->rNumericArgs(2 * genCount - 1),
     930           85 :                                                                  s_ipsc->cAlphaArgs(alphaCount + 2),
     931          255 :                                                                  s_ipsc->rNumericArgs(2 * genCount)));
     932           85 :             ++alphaCount;
     933           85 :             ++alphaCount;
     934           85 :             ++alphaCount;
     935              :         }
     936              : 
     937              :         // issue #5299 check for non-zero values in thermal electric ratio if gen op scheme is ThermalFollow*
     938           21 :         if (genOperationScheme_ == GeneratorOpScheme::ThermalFollow || genOperationScheme_ == GeneratorOpScheme::ThermalFollowLimitElectrical) {
     939              :             // check to make sure the user didn't input zeros for thermalToElectricControlRatio
     940            2 :             for (auto const &g : elecGenCntrlObj) {
     941            1 :                 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            1 :             }
     949              :         }
     950              :     }
     951              : 
     952           25 :     if (!errorsFound && inverterPresent) {
     953              :         // call inverter constructor
     954           12 :         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           12 :         if (inverterObj->modelType() == DCtoACInverter::InverterModelType::PVWatts) {
     959            1 :             Real64 totalDCCapacity = 0.0;
     960            4 :             for (const auto &generatorController : elecGenCntrlObj) {
     961            3 :                 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            3 :                     totalDCCapacity += generatorController->pvwattsGenerator->getDCSystemCapacity();
     971              : 
     972              :                     // Pass the inverter properties to the PVWatts generator class
     973            3 :                     generatorController->pvwattsGenerator->setDCtoACRatio(inverterObj->pvWattsDCtoACSizeRatio());
     974            3 :                     generatorController->pvwattsGenerator->setInverterEfficiency(inverterObj->pvWattsInverterEfficiency());
     975              :                 }
     976            1 :             }
     977            1 :             if (!errorsFound) {
     978            1 :                 inverterObj->setPVWattsDCCapacity(state, totalDCCapacity);
     979              :             }
     980              :         }
     981              :     }
     982              : 
     983           25 :     if (!errorsFound && storagePresent_) {
     984              :         // call storage constructor
     985            9 :         storageObj = std::make_unique<ElectricStorage>(state, storageName_);
     986              :     }
     987              : 
     988           25 :     if (!errorsFound && transformerPresent_) {
     989              : 
     990            0 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
     991            0 :         int transformerItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, s_ipsc->cCurrentModuleObject, transformerName_);
     992            0 :         if (transformerItemNum > 0) {
     993              :             int iOStat;
     994            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     995            0 :                                                                      s_ipsc->cCurrentModuleObject,
     996              :                                                                      transformerItemNum,
     997            0 :                                                                      s_ipsc->cAlphaArgs,
     998              :                                                                      numAlphas,
     999            0 :                                                                      s_ipsc->rNumericArgs,
    1000              :                                                                      numNums,
    1001              :                                                                      iOStat,
    1002            0 :                                                                      s_ipsc->lNumericFieldBlanks,
    1003            0 :                                                                      s_ipsc->lAlphaFieldBlanks,
    1004            0 :                                                                      s_ipsc->cAlphaFieldNames,
    1005            0 :                                                                      s_ipsc->cNumericFieldNames);
    1006            0 :             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            0 :                 ShowWarningError(
    1011              :                     state,
    1012            0 :                     format("Transformer named {} associated with the load center named {} should have {} set to LoadCenterPowerConditioning.",
    1013            0 :                            transformerName_,
    1014            0 :                            name_,
    1015            0 :                            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           25 :     if (!errorsFound && converterPresent_) {
    1024              :         // call AC to DC converter constructor
    1025            4 :         converterObj = std::make_unique<ACtoDCConverter>(state, converterName_);
    1026              :     }
    1027              : 
    1028              :     // Setup general output variables for reporting in the electric load center
    1029           50 :     SetupOutputVariable(state,
    1030              :                         "Electric Load Center Produced Electricity Rate",
    1031              :                         Constant::Units::W,
    1032           25 :                         genElectProdRate,
    1033              :                         OutputProcessor::TimeStepType::System,
    1034              :                         OutputProcessor::StoreType::Average,
    1035           25 :                         name_);
    1036           50 :     SetupOutputVariable(state,
    1037              :                         "Electric Load Center Produced Electricity Energy",
    1038              :                         Constant::Units::J,
    1039           25 :                         genElectricProd,
    1040              :                         OutputProcessor::TimeStepType::System,
    1041              :                         OutputProcessor::StoreType::Sum,
    1042           25 :                         name_);
    1043           50 :     SetupOutputVariable(state,
    1044              :                         "Electric Load Center Supplied Electricity Rate",
    1045              :                         Constant::Units::W,
    1046           25 :                         subpanelFeedInRate,
    1047              :                         OutputProcessor::TimeStepType::System,
    1048              :                         OutputProcessor::StoreType::Average,
    1049           25 :                         name_);
    1050           50 :     SetupOutputVariable(state,
    1051              :                         "Electric Load Center Drawn Electricity Rate",
    1052              :                         Constant::Units::W,
    1053           25 :                         subpanelDrawRate,
    1054              :                         OutputProcessor::TimeStepType::System,
    1055              :                         OutputProcessor::StoreType::Average,
    1056           25 :                         name_);
    1057           50 :     SetupOutputVariable(state,
    1058              :                         "Electric Load Center Produced Thermal Rate",
    1059              :                         Constant::Units::W,
    1060           25 :                         thermalProdRate,
    1061              :                         OutputProcessor::TimeStepType::System,
    1062              :                         OutputProcessor::StoreType::Average,
    1063           25 :                         name_);
    1064           50 :     SetupOutputVariable(state,
    1065              :                         "Electric Load Center Produced Thermal Energy",
    1066              :                         Constant::Units::J,
    1067           25 :                         thermalProd,
    1068              :                         OutputProcessor::TimeStepType::System,
    1069              :                         OutputProcessor::StoreType::Sum,
    1070           25 :                         name_);
    1071           50 :     SetupOutputVariable(state,
    1072              :                         "Electric Load Center Requested Electricity Rate",
    1073              :                         Constant::Units::W,
    1074           25 :                         totalPowerRequest_,
    1075              :                         OutputProcessor::TimeStepType::System,
    1076              :                         OutputProcessor::StoreType::Average,
    1077           25 :                         name_);
    1078              : 
    1079           25 :     if (state.dataGlobal->AnyEnergyManagementSystemInModel && storagePresent_) {
    1080            2 :         SetupEMSActuator(state, "Electrical Storage", name_, "Power Draw Rate", "[W]", eMSOverridePelFromStorage_, eMSValuePelFromStorage_);
    1081            2 :         SetupEMSActuator(state, "Electrical Storage", name_, "Power Charge Rate", "[W]", eMSOverridePelIntoStorage_, eMSValuePelIntoStorage_);
    1082              :     }
    1083              : 
    1084           25 :     if (errorsFound) {
    1085            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    1086              :     }
    1087            0 : }
    1088              : 
    1089      9337227 : void ElectPowerLoadCenter::manageElecLoadCenter(EnergyPlusData &state, bool const firstHVACIteration, Real64 &remainingWholePowerDemand)
    1090              : {
    1091              :     //
    1092      9337227 :     subpanelFeedInRequest = remainingWholePowerDemand;
    1093              : 
    1094      9337227 :     if (generatorsPresent_) {
    1095       193566 :         dispatchGenerators(state, firstHVACIteration, remainingWholePowerDemand);
    1096              : 
    1097              :     } // if generators present
    1098      9337227 :     updateLoadCenterGeneratorRecords(state);
    1099      9337227 :     if (bussType == ElectricBussType::DCBussInverter || bussType == ElectricBussType::DCBussInverterACStorage) {
    1100        15232 :         inverterObj->simulate(state, genElectProdRate);
    1101              :     }
    1102              : 
    1103      9337227 :     if (storagePresent_) {
    1104       130975 :         storageObj->timeCheckAndUpdate(state);
    1105       130975 :         dispatchStorage(state, subpanelFeedInRequest);
    1106              :     }
    1107              : 
    1108      9337227 :     if (bussType == ElectricBussType::DCBussInverterDCStorage) {
    1109       130975 :         if (inverterObj != nullptr) {
    1110       130975 :             inverterObj->simulate(state, storOpCVFeedInRate);
    1111              :         }
    1112              :     }
    1113              : 
    1114      9337227 :     if (converterObj != nullptr) {
    1115        69372 :         converterObj->simulate(state, storOpCVDrawRate);
    1116              :     }
    1117              : 
    1118      9337227 :     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      9337227 :     updateLoadCenterGeneratorRecords(state);
    1126      9337227 : }
    1127              : 
    1128       193566 : 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       193566 :     Real64 loadCenterElectricLoad = 0.0;
    1144       193566 :     Real64 remainingLoad = 0.0;
    1145       193566 :     Real64 customMeterDemand = 0.0;
    1146              : 
    1147       193566 :     switch (genOperationScheme_) {
    1148              : 
    1149        86024 :     case GeneratorOpScheme::BaseLoad: {
    1150              : 
    1151        86024 :         loadCenterElectricLoad = remainingWholePowerDemand;
    1152              : 
    1153       353825 :         for (auto &g : elecGenCntrlObj) {
    1154              : 
    1155       267801 :             if (g->availSched->getCurrentVal() > 0.0) {
    1156              :                 // Set the Operation Flag
    1157       201816 :                 g->onThisTimestep = true;
    1158              :                 // Set the electric generator load request
    1159       201816 :                 g->powerRequestThisTimestep = g->maxPowerOut;
    1160              :             } else {
    1161        65985 :                 g->onThisTimestep = false;
    1162        65985 :                 g->powerRequestThisTimestep = 0.0;
    1163              :             }
    1164              : 
    1165              :             // now handle EMS override
    1166       267801 :             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       267801 :             g->simGeneratorGetPowerOutput(
    1177       267801 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1178              : 
    1179       267801 :             totalPowerRequest_ += g->powerRequestThisTimestep;
    1180       267801 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1181        86024 :         }
    1182        86024 :         break;
    1183              :     }
    1184        16371 :     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        16371 :         remainingLoad = remainingWholePowerDemand - demandLimit_;
    1188        16371 :         loadCenterElectricLoad = remainingLoad;
    1189              : 
    1190        49758 :         for (auto &g : elecGenCntrlObj) {
    1191              : 
    1192        33387 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1193              :                 // Set the Operation Flag
    1194         6752 :                 g->onThisTimestep = true;
    1195              : 
    1196              :                 // Set the electric generator load
    1197         6752 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1198              : 
    1199              :                 // now handle EMS override
    1200         6752 :                 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        26635 :                 g->onThisTimestep = false;
    1210        26635 :                 g->powerRequestThisTimestep = 0.0;
    1211              : 
    1212              :                 // now handle EMS override
    1213        26635 :                 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        33387 :             g->simGeneratorGetPowerOutput(
    1225        33387 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1226              : 
    1227        33387 :             if (g->eMSRequestOn) {
    1228            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1229              :             } else {
    1230        33387 :                 if (g->powerRequestThisTimestep > 0.0) {
    1231         6752 :                     totalPowerRequest_ += g->maxPowerOut;
    1232         6752 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1233              :                 }
    1234              :             }
    1235        33387 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1236        33387 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1237        16371 :         }
    1238        16371 :         break;
    1239              :     }
    1240        73756 :     case GeneratorOpScheme::TrackElectrical: {
    1241              :         // The Track Electrical scheme tries to have the generators meet all of the electrical demand for the building.
    1242        73756 :         remainingLoad = remainingWholePowerDemand;
    1243        73756 :         loadCenterElectricLoad = remainingLoad;
    1244              : 
    1245       541664 :         for (auto &g : elecGenCntrlObj) {
    1246              : 
    1247       467908 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1248              :                 // Set the Operation Flag
    1249       333331 :                 g->onThisTimestep = true;
    1250              : 
    1251              :                 // Set the electric generator load
    1252       333331 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1253              : 
    1254              :                 // now handle EMS override
    1255       333331 :                 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       134577 :                 g->onThisTimestep = false;
    1265       134577 :                 g->powerRequestThisTimestep = 0.0;
    1266              :                 // now handle EMS override
    1267       134577 :                 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       467908 :             g->simGeneratorGetPowerOutput(
    1279       467908 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1280              : 
    1281       467908 :             if (g->eMSRequestOn) {
    1282            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1283              :             } else {
    1284       467908 :                 if (g->powerRequestThisTimestep > 0.0) {
    1285       333331 :                     totalPowerRequest_ += g->maxPowerOut;
    1286       333331 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1287              :                 }
    1288              :             }
    1289       467908 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1290       467908 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1291        73756 :         }
    1292        73756 :         break;
    1293              :     }
    1294         6135 :     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         6135 :         remainingLoad = trackSched_->getCurrentVal();
    1299         6135 :         loadCenterElectricLoad = remainingLoad;
    1300              : 
    1301        12270 :         for (auto &g : elecGenCntrlObj) {
    1302              : 
    1303         6135 :             if (g->availSched->getCurrentVal() > 0.0 && remainingLoad > 0.0) {
    1304              :                 // Set the Operation Flag
    1305          504 :                 g->onThisTimestep = true;
    1306              : 
    1307              :                 // Set the electric generator load
    1308          504 :                 g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1309              : 
    1310              :                 // now handle EMS override
    1311          504 :                 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         5631 :                 g->onThisTimestep = false;
    1321         5631 :                 g->powerRequestThisTimestep = 0.0;
    1322              : 
    1323              :                 // now handle EMS override
    1324         5631 :                 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         6135 :             g->simGeneratorGetPowerOutput(
    1336         6135 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1337              : 
    1338         6135 :             if (g->eMSRequestOn) {
    1339            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1340              :             } else {
    1341         6135 :                 if (g->powerRequestThisTimestep > 0.0) {
    1342          504 :                     totalPowerRequest_ += g->maxPowerOut;
    1343          504 :                     totalPowerRequest_ = min(loadCenterElectricLoad, totalPowerRequest_);
    1344              :                 }
    1345              :             }
    1346         6135 :             remainingLoad -= g->electProdRate;             // Update remaining load to be met by this load center
    1347         6135 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1348         6135 :         }
    1349         6135 :         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        11280 :     case GeneratorOpScheme::ThermalFollow: {
    1411              :         // Turn thermal load into an electrical load for cogenerators controlled to follow heat loads
    1412        11280 :         Real64 remainingThermalLoad = calcLoadCenterThermalLoad(state);
    1413        11280 :         Real64 loadCenterThermalLoad = remainingThermalLoad;
    1414        22560 :         for (auto &g : elecGenCntrlObj) {
    1415              : 
    1416        11280 :             if (g->availSched->getCurrentVal() > 0.0 && remainingThermalLoad > 0.0) {
    1417              : 
    1418         4640 :                 if (g->nominalThermElectRatio > 0.0) {
    1419         4640 :                     remainingLoad = remainingThermalLoad / g->nominalThermElectRatio;
    1420         4640 :                     g->powerRequestThisTimestep = min(g->maxPowerOut, remainingLoad);
    1421         4640 :                     g->onThisTimestep = true;
    1422              :                     // now handle EMS override
    1423         4640 :                     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         6640 :                 g->onThisTimestep = false;
    1434         6640 :                 g->powerRequestThisTimestep = 0.0;
    1435              :                 // now handle EMS override
    1436         6640 :                 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        11280 :             g->simGeneratorGetPowerOutput(
    1448        11280 :                 state, g->onThisTimestep, g->powerRequestThisTimestep, firstHVACIteration, g->electProdRate, g->thermProdRate);
    1449              : 
    1450        11280 :             if (g->eMSRequestOn) {
    1451            0 :                 totalThermalPowerRequest_ += max(g->eMSPowerRequest, 0.0) * g->nominalThermElectRatio;
    1452            0 :                 totalPowerRequest_ += max(g->eMSPowerRequest, 0.0);
    1453              :             } else {
    1454        11280 :                 if (totalThermalPowerRequest_ < loadCenterThermalLoad && g->powerRequestThisTimestep > 0.0) {
    1455            4 :                     Real64 excessThermalPowerRequest = totalThermalPowerRequest_ + g->maxPowerOut * g->nominalThermElectRatio - loadCenterThermalLoad;
    1456            4 :                     if (excessThermalPowerRequest < 0.0) {
    1457            2 :                         totalThermalPowerRequest_ += g->maxPowerOut * g->nominalThermElectRatio;
    1458            2 :                         totalPowerRequest_ += g->maxPowerOut;
    1459              :                     } else {
    1460            2 :                         totalThermalPowerRequest_ = loadCenterThermalLoad;
    1461            2 :                         if (g->nominalThermElectRatio > 0.0) {
    1462            2 :                             totalPowerRequest_ += g->maxPowerOut - (excessThermalPowerRequest / g->nominalThermElectRatio);
    1463              :                         }
    1464              :                     }
    1465              :                 }
    1466              :             }
    1467        11280 :             remainingThermalLoad -= g->thermProdRate; // Update remaining load to be met
    1468              :             // by this load center
    1469        11280 :             remainingWholePowerDemand -= g->electProdRate; // Update whole building remaining load
    1470        11280 :         }
    1471        11280 :         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       193566 :     genElectProdRate = 0.0;
    1549       193566 :     genElectricProd = 0.0;
    1550       980077 :     for (auto const &g : elecGenCntrlObj) {
    1551       786511 :         genElectProdRate += g->electProdRate;
    1552       786511 :         g->electricityProd = g->electProdRate * (state.dataHVACGlobal->TimeStepSysSec);
    1553       786511 :         genElectricProd += g->electricityProd;
    1554       193566 :     }
    1555       193566 : }
    1556              : 
    1557       130975 : 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       130975 :     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       130975 :     case ElectricBussType::DCBussInverterDCStorage: {
    1575       130975 :         storOpCVGenRate = genElectProdRate;
    1576       130975 :         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       130975 :     Real64 subpanelFeedInRequest = 0.0;
    1589       130975 :     Real64 subpanelDrawRequest = 0.0;
    1590       130975 :     switch (storageScheme_) {
    1591            0 :     case StorageOpScheme::Invalid: {
    1592              :         // do nothing
    1593            0 :         break;
    1594              :     }
    1595        61603 :     case StorageOpScheme::FacilityDemandStoreExcessOnSite: {
    1596        61603 :         subpanelFeedInRequest = originalFeedInRequest; // legacy behavior, storage dispatched to meet building load
    1597        61603 :         subpanelDrawRequest = 0.0;
    1598        61603 :         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        52029 :     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        52029 :         break;
    1611              :     }
    1612        17343 :     case StorageOpScheme::FacilityDemandLeveling: {
    1613        17343 :         Real64 demandTarget = facilityDemandTarget_ * facilityDemandTargetModSched_->getCurrentVal();
    1614              :         // compare target to
    1615        17343 :         Real64 deltaLoad = originalFeedInRequest - demandTarget;
    1616        17343 :         if (deltaLoad >= 0.0) {
    1617              :             // subpanel should feed main panel
    1618         7814 :             subpanelFeedInRequest = deltaLoad;
    1619         7814 :             subpanelDrawRequest = 0.0;
    1620              :         } else {
    1621              :             // subpanel should draw from main panel
    1622         9529 :             subpanelFeedInRequest = 0.0;
    1623         9529 :             subpanelDrawRequest = std::abs(deltaLoad);
    1624              :         }
    1625        17343 :         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       130975 :     Real64 adjustedFeedInRequest = 0.0; // account for any inverter or transformer losses
    1633       130975 :     Real64 adjustedDrawRequest = 0.0;   // account for any converer or transformer losses
    1634              : 
    1635       130975 :     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       130975 :     case ElectricBussType::DCBussInverterDCStorage: {
    1654              :         // can we get updated power conditioning losses here?
    1655       130975 :         if (transformerObj == nullptr) {
    1656       130975 :             adjustedFeedInRequest = subpanelFeedInRequest + inverterObj->getLossRateForOutputPower(state, subpanelFeedInRequest);
    1657       130975 :             if (converterObj == nullptr) { // some operation schemes will never need a converter
    1658        61603 :                 adjustedDrawRequest = subpanelDrawRequest;
    1659              :             } else {
    1660        69372 :                 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       130975 :         break;
    1673              :     }
    1674            0 :     default:
    1675            0 :         assert(false);
    1676              :     } // end switch buss type
    1677              : 
    1678       130975 :     switch (storageScheme_) {
    1679            0 :     case StorageOpScheme::Invalid: {
    1680              :         // do nothing
    1681            0 :         break;
    1682              :     }
    1683        61603 :     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        61603 :         storOpCVDrawRate = 0.0;
    1690              : 
    1691        61603 :         if (storOpCVGenRate < adjustedFeedInRequest) {
    1692              :             // draw from storage
    1693        40431 :             storOpCVDischargeRate = adjustedFeedInRequest - storOpCVGenRate;
    1694        40431 :             storOpCVChargeRate = 0.0;
    1695        40431 :             storOpIsDischarging = true;
    1696        40431 :             storOpIsCharging = false;
    1697              : 
    1698        21172 :         } else if (storOpCVGenRate > adjustedFeedInRequest) {
    1699              :             // add to storage
    1700        12305 :             storOpCVDischargeRate = 0.0;
    1701        12305 :             storOpCVChargeRate = storOpCVGenRate - adjustedFeedInRequest;
    1702        12305 :             storOpIsCharging = true;
    1703        12305 :             storOpIsDischarging = false;
    1704              : 
    1705         8867 :         } else if (storOpCVGenRate == adjustedFeedInRequest) {
    1706              :             // do nothing
    1707         8867 :             storOpCVDischargeRate = 0.0;
    1708         8867 :             storOpCVChargeRate = 0.0;
    1709         8867 :             storOpIsCharging = false;
    1710         8867 :             storOpIsDischarging = false;
    1711              :         }
    1712        61603 :         break;
    1713              :     }
    1714              : 
    1715        52029 :     case StorageOpScheme::ChargeDischargeSchedules: {
    1716        52029 :         storOpCVChargeRate = designStorageChargePower_ * storageChargeModSched_->getCurrentVal();
    1717        52029 :         storOpCVDischargeRate = designStorageDischargePower_ * storageDischargeModSched_->getCurrentVal();
    1718        52029 :         Real64 genAndStorSum = storOpCVGenRate + storOpCVDischargeRate - storOpCVChargeRate;
    1719        52029 :         if (genAndStorSum >= 0.0) { // power to feed toward main panel
    1720        11760 :             storOpCVDrawRate = 0.0;
    1721        11760 :             storOpCVFeedInRate = genAndStorSum;
    1722              :         } else { // shortfall, will need to draw from main panel (e.g. for grid charging)
    1723        40269 :             storOpCVFeedInRate = 0.0;
    1724        40269 :             storOpCVDrawRate = std::abs(genAndStorSum);
    1725              :         }
    1726        52029 :         if (storOpCVChargeRate > 0.0) {
    1727        40269 :             storOpIsCharging = true;
    1728              :         } else {
    1729        11760 :             storOpIsCharging = false;
    1730              :         }
    1731        52029 :         if (storOpCVDischargeRate > 0.0) {
    1732        10080 :             storOpIsDischarging = true;
    1733              :         } else {
    1734        41949 :             storOpIsDischarging = false;
    1735              :         }
    1736        52029 :         break;
    1737              :     }
    1738        17343 :     case StorageOpScheme::FacilityDemandLeveling: {
    1739              : 
    1740        17343 :         if (adjustedDrawRequest > 0.0) { // the only reason to draw instead of feed is to charge storage
    1741         9529 :             storOpCVFeedInRate = 0.0;
    1742         9529 :             storOpCVDrawRate = adjustedDrawRequest;
    1743         9529 :             storOpCVChargeRate = storOpCVDrawRate + storOpCVGenRate;
    1744         9529 :             storOpCVDischargeRate = 0.0;
    1745         9529 :             storOpIsCharging = true;
    1746         9529 :             storOpIsDischarging = false;
    1747              :         }
    1748        17343 :         if (adjustedFeedInRequest > 0.0) {
    1749         7814 :             storOpCVDrawRate = 0.0;
    1750         7814 :             storOpCVFeedInRate = adjustedFeedInRequest;
    1751         7814 :             if (storOpCVGenRate < adjustedFeedInRequest) {
    1752              :                 // draw from storage
    1753         7814 :                 storOpCVDischargeRate = adjustedFeedInRequest - storOpCVGenRate;
    1754         7814 :                 storOpCVChargeRate = 0.0;
    1755         7814 :                 storOpIsDischarging = true;
    1756         7814 :                 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        17343 :         break;
    1774              :     }
    1775       130975 :     default:
    1776              :         assert(true);
    1777              :     }
    1778              : 
    1779              :     // handle EMS overrides
    1780       130975 :     if (eMSOverridePelFromStorage_ || eMSOverridePelIntoStorage_) {
    1781        32374 :         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        32374 :         } 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        32374 :         } else if (eMSOverridePelFromStorage_ && eMSOverridePelIntoStorage_) {
    1794              :             // EMS is asking to override both
    1795        32374 :             if (eMSValuePelIntoStorage_ > eMSValuePelFromStorage_) {
    1796         2757 :                 storOpCVChargeRate = eMSValuePelIntoStorage_ - eMSValuePelFromStorage_;
    1797         2757 :                 storOpCVDischargeRate = 0.0;
    1798         2757 :                 storOpIsDischarging = false;
    1799         2757 :                 storOpIsCharging = true;
    1800        29617 :             } else if (eMSValuePelIntoStorage_ < eMSValuePelFromStorage_) {
    1801        26252 :                 storOpCVDischargeRate = eMSValuePelFromStorage_ - eMSValuePelIntoStorage_;
    1802        26252 :                 storOpCVChargeRate = 0.0;
    1803        26252 :                 storOpIsDischarging = true;
    1804        26252 :                 storOpIsCharging = false;
    1805              :             } else { // they equal just hold
    1806         3365 :                 storOpCVDischargeRate = 0.0;
    1807         3365 :                 storOpCVChargeRate = 0.0;
    1808         3365 :                 storOpIsDischarging = false;
    1809         3365 :                 storOpIsCharging = false;
    1810              :             }
    1811              :         }
    1812              :     }
    1813              : 
    1814              :     // check against the controller limits
    1815       130975 :     if (designStorageChargePowerWasSet_) {
    1816        69372 :         storOpCVChargeRate = min(storOpCVChargeRate, designStorageChargePower_);
    1817              :     }
    1818       130975 :     if (designStorageDischargePowerWasSet_) {
    1819        69372 :         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       130975 :     storageObj->simulate(
    1825       130975 :         state, storOpCVChargeRate, storOpCVDischargeRate, storOpIsCharging, storOpIsDischarging, maxStorageSOCFraction_, minStorageSOCFraction_);
    1826              : 
    1827              :     // rebalance with final charge and discharge rates
    1828       130975 :     Real64 genAndStorSum = storOpCVGenRate + storOpCVDischargeRate - storOpCVChargeRate;
    1829       130975 :     if (genAndStorSum >= 0.0) { // power to feed toward main panel
    1830       103714 :         storOpCVDrawRate = 0.0;
    1831       103714 :         storOpCVFeedInRate = genAndStorSum;
    1832              :     } else { // shortfall, will need to draw from main panel (e.g. for grid charging)
    1833        27261 :         storOpCVFeedInRate = 0.0;
    1834        27261 :         storOpCVDrawRate = std::abs(genAndStorSum);
    1835              :     }
    1836       130975 : }
    1837              : 
    1838         1468 : void ElectPowerLoadCenter::setupLoadCenterMeterIndices(EnergyPlusData &state)
    1839              : {
    1840         1468 :     demandMeterPtr_ = EnergyPlus::GetMeterIndex(state, demandMeterName_);
    1841         1468 :     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         1468 :     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         1468 : }
    1860              : 
    1861         6097 : void ElectPowerLoadCenter::reinitAtBeginEnvironment()
    1862              : {
    1863         6097 :     genElectricProd = 0.0;
    1864         6097 :     genElectProdRate = 0.0;
    1865         6097 :     thermalProd = 0.0;
    1866         6097 :     thermalProdRate = 0.0;
    1867         6097 :     totalPowerRequest_ = 0.0;
    1868         6097 :     totalThermalPowerRequest_ = 0.0;
    1869         6097 :     subpanelFeedInRate = 0.0;
    1870         6097 :     subpanelDrawRate = 0.0;
    1871         6097 :     storOpCVDrawRate = 0.0;
    1872         6097 :     storOpCVFeedInRate = 0.0;
    1873         6097 :     storOpCVChargeRate = 0.0;
    1874         6097 :     storOpCVDischargeRate = 0.0;
    1875              : 
    1876         6097 :     if (generatorsPresent_ && numGenerators > 0) {
    1877          820 :         for (auto &g : elecGenCntrlObj) {
    1878          661 :             g->reinitAtBeginEnvironment();
    1879          159 :         }
    1880              :     }
    1881              : 
    1882         6097 :     if (transformerObj != nullptr) {
    1883            0 :         transformerObj->reinitAtBeginEnvironment();
    1884              :     }
    1885              : 
    1886         6097 :     if (storageObj != nullptr) {
    1887           83 :         storageObj->reinitAtBeginEnvironment();
    1888              :     }
    1889              : 
    1890         6097 :     if (inverterObj != nullptr) {
    1891          104 :         inverterObj->reinitAtBeginEnvironment();
    1892              :     }
    1893              : 
    1894         6097 :     if (converterObj != nullptr) {
    1895           36 :         converterObj->reinitAtBeginEnvironment();
    1896              :     }
    1897         6097 : }
    1898              : 
    1899         5362 : void ElectPowerLoadCenter::reinitZoneGainsAtBeginEnvironment()
    1900              : {
    1901         5362 :     if (transformerObj != nullptr) {
    1902            0 :         transformerObj->reinitZoneGainsAtBeginEnvironment();
    1903              :     }
    1904              : 
    1905         5362 :     if (storageObj != nullptr) {
    1906           74 :         storageObj->reinitZoneGainsAtBeginEnvironment();
    1907              :     }
    1908              : 
    1909         5362 :     if (inverterObj != nullptr) {
    1910           92 :         inverterObj->reinitZoneGainsAtBeginEnvironment();
    1911              :     }
    1912              : 
    1913         5362 :     if (converterObj != nullptr) {
    1914           32 :         converterObj->reinitZoneGainsAtBeginEnvironment();
    1915              :     }
    1916         5362 : }
    1917              : 
    1918           21 : std::string const &ElectPowerLoadCenter::generatorListName() const
    1919              : {
    1920           21 :     return generatorListName_;
    1921              : }
    1922              : 
    1923     18674454 : void ElectPowerLoadCenter::updateLoadCenterGeneratorRecords(EnergyPlusData &state)
    1924              : {
    1925              : 
    1926     18674454 :     switch (bussType) {
    1927       233462 :     case ElectricBussType::ACBuss: {
    1928       233462 :         genElectProdRate = 0.0;
    1929       233462 :         genElectricProd = 0.0;
    1930       649420 :         for (auto const &gc : elecGenCntrlObj) {
    1931       415958 :             genElectProdRate += gc->electProdRate;
    1932       415958 :             genElectricProd += gc->electricityProd;
    1933       233462 :         }
    1934              :         // no inverter, no storage, so generator production equals subpanel feed in
    1935       233462 :         subpanelFeedInRate = genElectProdRate;
    1936       233462 :         if (transformerObj != nullptr) {
    1937            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, genElectProdRate);
    1938              :         }
    1939       233462 :         subpanelDrawRate = 0.0;
    1940              : 
    1941       233462 :         break;
    1942              :     }
    1943            0 :     case ElectricBussType::ACBussStorage: {
    1944            0 :         genElectProdRate = 0.0;
    1945            0 :         genElectricProd = 0.0;
    1946            0 :         for (auto const &gc : elecGenCntrlObj) {
    1947            0 :             genElectProdRate += gc->electProdRate;
    1948            0 :             genElectricProd += gc->electricityProd;
    1949            0 :         }
    1950            0 :         if (storageObj != nullptr) {
    1951            0 :             subpanelFeedInRate = genElectProdRate + storOpCVDischargeRate - storOpCVChargeRate;
    1952              :         } else {
    1953            0 :             subpanelFeedInRate = genElectProdRate;
    1954              :         }
    1955            0 :         if (transformerObj != nullptr) {
    1956            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1957              :         }
    1958            0 :         subpanelDrawRate = 0.0;
    1959            0 :         break;
    1960              :     }
    1961        30464 :     case ElectricBussType::DCBussInverter: {
    1962        30464 :         genElectProdRate = 0.0;
    1963        30464 :         genElectricProd = 0.0;
    1964       276018 :         for (auto const &gc : elecGenCntrlObj) {
    1965       245554 :             genElectProdRate += gc->electProdRate;
    1966       245554 :             genElectricProd += gc->electricityProd;
    1967        30464 :         }
    1968              : 
    1969        30464 :         if (inverterObj != nullptr) {
    1970        30464 :             subpanelFeedInRate = inverterObj->aCPowerOut();
    1971              :         }
    1972        30464 :         if (transformerObj != nullptr) {
    1973            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1974              :         }
    1975        30464 :         subpanelDrawRate = 0.0;
    1976        30464 :         break;
    1977              :     }
    1978              : 
    1979       261950 :     case ElectricBussType::DCBussInverterDCStorage: {
    1980       261950 :         genElectProdRate = 0.0;
    1981       261950 :         genElectricProd = 0.0;
    1982      1173460 :         for (auto const &gc : elecGenCntrlObj) {
    1983       911510 :             genElectProdRate += gc->electProdRate;
    1984       911510 :             genElectricProd += gc->electricityProd;
    1985       261950 :         }
    1986       261950 :         if (inverterObj != nullptr) {
    1987       261950 :             subpanelFeedInRate = inverterObj->aCPowerOut();
    1988              :         }
    1989              : 
    1990       261950 :         if (converterObj != nullptr) {
    1991       138744 :             subpanelDrawRate = converterObj->aCPowerIn();
    1992              :         }
    1993       261950 :         if (transformerObj != nullptr) {
    1994            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    1995            0 :             subpanelDrawRate += transformerObj->getLossRateForOutputPower(state, subpanelDrawRate);
    1996              :         }
    1997       261950 :         break;
    1998              :     }
    1999            0 :     case ElectricBussType::DCBussInverterACStorage: {
    2000            0 :         genElectProdRate = 0.0;
    2001            0 :         genElectricProd = 0.0;
    2002            0 :         for (auto const &gc : elecGenCntrlObj) {
    2003            0 :             genElectProdRate += gc->electProdRate;
    2004            0 :             genElectricProd += gc->electricityProd;
    2005            0 :         }
    2006            0 :         if (inverterObj != nullptr && storagePresent_) {
    2007            0 :             subpanelFeedInRate = inverterObj->aCPowerOut() + storOpCVDischargeRate - storOpCVChargeRate;
    2008              :         }
    2009              : 
    2010            0 :         subpanelDrawRate = storOpCVDrawRate; // no converter for AC storage
    2011            0 :         if (transformerObj != nullptr) {
    2012            0 :             subpanelFeedInRate -= transformerObj->getLossRateForInputPower(state, subpanelFeedInRate);
    2013            0 :             subpanelDrawRate += transformerObj->getLossRateForOutputPower(state, subpanelDrawRate);
    2014              :         }
    2015            0 :         break;
    2016              :     }
    2017     18148578 :     case ElectricBussType::Invalid: {
    2018              :         // do nothing
    2019     18148578 :         break;
    2020              :     }
    2021            0 :     default:
    2022            0 :         assert(false);
    2023              :     } // end switch
    2024     18674454 :     thermalProdRate = 0.0;
    2025     18674454 :     thermalProd = 0.0;
    2026     20247476 :     for (auto const &gc : elecGenCntrlObj) {
    2027      1573022 :         thermalProdRate += gc->thermProdRate;
    2028      1573022 :         thermalProd += gc->thermalProd;
    2029     18674454 :     }
    2030     18674454 : }
    2031              : 
    2032        11280 : Real64 ElectPowerLoadCenter::calcLoadCenterThermalLoad(EnergyPlusData &state)
    2033              : {
    2034        11280 :     if (myCoGenSetupFlag_) {
    2035            2 :         for (auto &g : elecGenCntrlObj) {
    2036            1 :             bool plantNotFound = false;
    2037            1 :             PlantUtilities::ScanPlantLoopsForObject(state, g->compPlantName, g->compPlantType, g->cogenLocation, plantNotFound, _, _, _, _, _);
    2038            1 :             if (!plantNotFound) {
    2039            1 :                 g->plantInfoFound = true;
    2040              :             }
    2041            1 :         }
    2042            1 :         myCoGenSetupFlag_ = false;
    2043              :     } // cogen setup
    2044              : 
    2045              :     // sum up "MyLoad" for all generators on this load center from plant structure
    2046        11280 :     Real64 thermalLoad = 0.0;
    2047        22560 :     for (auto &g : elecGenCntrlObj) {
    2048        11280 :         if (g->plantInfoFound) {
    2049        11280 :             thermalLoad += state.dataPlnt->PlantLoop(g->cogenLocation.loopNum)
    2050        11280 :                                .LoopSide(g->cogenLocation.loopSideNum)
    2051        11280 :                                .Branch(g->cogenLocation.branchNum)
    2052        11280 :                                .Comp(g->cogenLocation.compNum)
    2053        11280 :                                .MyLoad;
    2054              :         }
    2055        11280 :     }
    2056        11280 :     return thermalLoad;
    2057              : }
    2058              : 
    2059              : // TODO: Constructors should not do this much work
    2060           85 : GeneratorController::GeneratorController(EnergyPlusData &state,
    2061              :                                          std::string const &objectName,
    2062              :                                          std::string const &objectType,
    2063              :                                          Real64 ratedElecPowerOutput,
    2064              :                                          std::string const &availSchedName,
    2065           85 :                                          Real64 thermalToElectRatio)
    2066          170 :     : generatorType(GeneratorType::Invalid), compPlantType(DataPlant::PlantEquipmentType::Invalid), generatorIndex(0), maxPowerOut(0.0),
    2067           85 :       powerRequestThisTimestep(0.0), onThisTimestep(false), eMSPowerRequest(0.0), eMSRequestOn(false), plantInfoFound(false),
    2068          170 :       cogenLocation(PlantLocation(0, DataPlant::LoopSideLocation::Invalid, 0, 0)), nominalThermElectRatio(0.0), dCElectricityProd(0.0),
    2069           85 :       dCElectProdRate(0.0), electricityProd(0.0), electProdRate(0.0), thermalProd(0.0), thermProdRate(0.0), pvwattsGenerator(nullptr),
    2070           85 :       errCountNegElectProd_(0)
    2071              : {
    2072              : 
    2073              :     static constexpr std::string_view routineName = "GeneratorController constructor ";
    2074              : 
    2075           85 :     auto &s_ipsc = state.dataIPShortCut;
    2076              : 
    2077           85 :     ErrorObjectHeader eoh{routineName, objectType, objectName};
    2078              : 
    2079           85 :     name = objectName;
    2080              : 
    2081           85 :     generatorType = static_cast<GeneratorType>(getEnumValue(generatorTypeNamesUC, Util::makeUPPER(objectType)));
    2082           85 :     switch (generatorType) {
    2083            5 :     case GeneratorType::ICEngine: {
    2084            5 :         compPlantType = DataPlant::PlantEquipmentType::Generator_ICEngine;
    2085            5 :         compPlantName = name;
    2086            5 :         break;
    2087              :     }
    2088            6 :     case GeneratorType::CombTurbine: {
    2089            6 :         compPlantType = DataPlant::PlantEquipmentType::Generator_CTurbine;
    2090            6 :         compPlantName = name;
    2091            6 :         break;
    2092              :     }
    2093            6 :     case GeneratorType::Microturbine: {
    2094            6 :         compPlantType = DataPlant::PlantEquipmentType::Generator_MicroTurbine;
    2095            6 :         compPlantName = name;
    2096            6 :         break;
    2097              :     }
    2098           58 :     case GeneratorType::PV: {
    2099           58 :         compPlantType = DataPlant::PlantEquipmentType::PVTSolarCollectorFlatPlate;
    2100           58 :         compPlantName = name;
    2101           58 :         break;
    2102              :     }
    2103            3 :     case GeneratorType::PVWatts: {
    2104            3 :         compPlantType = DataPlant::PlantEquipmentType::Invalid;
    2105              : 
    2106            3 :         int ObjNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "Generator:PVWatts", Util::makeUPPER(objectName));
    2107            3 :         assert(ObjNum >= 0);
    2108            3 :         if (ObjNum == 0) {
    2109            0 :             ShowFatalError(state, format("Cannot find Generator:PVWatts {}", objectName));
    2110              :         }
    2111            3 :         pvwattsGenerator = PVWatts::PVWattsGenerator::createFromIdfObj(state, ObjNum);
    2112            3 :         pvwattsGenerator->setupOutputVariables(state);
    2113            3 :         break;
    2114              :     }
    2115            2 :     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            2 :         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            2 :         auto *thisFC = FuelCellElectricGenerator::FCDataStruct::factory(state, name);
    2121            2 :         compPlantName = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->ExhaustHX.Name;
    2122            2 :         break;
    2123              :     }
    2124            2 :     case GeneratorType::MicroCHP: {
    2125            2 :         compPlantType = DataPlant::PlantEquipmentType::Generator_MicroCHP;
    2126            2 :         compPlantName = name;
    2127            2 :         break;
    2128              :     }
    2129            3 :     case GeneratorType::WindTurbine: {
    2130            3 :         compPlantType = DataPlant::PlantEquipmentType::Invalid;
    2131            3 :         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           85 :     if (availSchedName.empty()) {
    2141           48 :         availSched = Sched::GetScheduleAlwaysOn(state);
    2142           37 :     } else if ((availSched = Sched::GetSchedule(state, availSchedName)) == nullptr) {
    2143            0 :         ShowSevereItemNotFound(state, eoh, "Availability Schedule Name", availSchedName);
    2144           37 :     } else if (generatorType == GeneratorType::PVWatts) {
    2145            0 :         ShowWarningCustom(state, eoh, "Availability Schedule will be ignored (runs all the time).");
    2146           37 :     } 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           13 :         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           13 :         Array1D_string Alphas(5);   // Alpha items for object
    2156           13 :         Array1D<Real64> Numbers(2); // Numeric items for object
    2157           13 :         state.dataInputProcessing->inputProcessor->getObjectItem(state, objectType, PVNum, Alphas, NumAlphas, Numbers, NumNums, IOStat);
    2158           13 :         if (Util::SameString(Alphas(3), "PhotovoltaicPerformance:Simple")) {
    2159            0 :             ShowWarningCustom(state, eoh, "Availability Schedule will be ignored (runs all the time).");
    2160            0 :             ShowContinueError(state, "To limit this Generator:Photovoltaic's output, please use the Inverter's availability schedule instead.");
    2161              :         }
    2162           13 :     }
    2163              : 
    2164           85 :     maxPowerOut = ratedElecPowerOutput, nominalThermElectRatio = thermalToElectRatio;
    2165              : 
    2166          170 :     SetupOutputVariable(state,
    2167              :                         "Generator Requested Electricity Rate",
    2168              :                         Constant::Units::W,
    2169           85 :                         powerRequestThisTimestep,
    2170              :                         OutputProcessor::TimeStepType::System,
    2171              :                         OutputProcessor::StoreType::Average,
    2172              :                         objectName);
    2173           85 :     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           85 : }
    2179              : 
    2180          661 : void GeneratorController::reinitAtBeginEnvironment()
    2181              : {
    2182          661 :     onThisTimestep = false;
    2183          661 :     dCElectricityProd = 0.0;
    2184          661 :     dCElectProdRate = 0.0;
    2185          661 :     electricityProd = 0.0;
    2186          661 :     electProdRate = 0.0;
    2187          661 :     thermalProd = 0.0;
    2188          661 :     thermProdRate = 0.0;
    2189          661 : }
    2190              : 
    2191       786511 : 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       786511 :     switch (generatorType) {
    2201        44136 :     case GeneratorType::ICEngine: {
    2202              : 
    2203        44136 :         auto *thisICE = ICEngineElectricGenerator::ICEngineGeneratorSpecs::factory(state, name);
    2204              : 
    2205              :         // dummy vars
    2206        44136 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2207        44136 :         Real64 tempLoad = myElecLoadRequest;
    2208              : 
    2209              :         // simulate
    2210        44136 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->InitICEngineGenerators(state, runFlag, FirstHVACIteration);
    2211        44136 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->CalcICEngineGeneratorModel(state, runFlag, tempLoad);
    2212        44136 :         dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->update(state);
    2213        44136 :         electProdRate = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->ElecPowerGenerated;
    2214        44136 :         electricityProd = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->ElecEnergyGenerated;
    2215        44136 :         thermProdRate = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->QTotalHeatRecovered;
    2216        44136 :         thermalProd = dynamic_cast<ICEngineElectricGenerator::ICEngineGeneratorSpecs *>(thisICE)->TotalHeatEnergyRec;
    2217        44136 :         electricPowerOutput = electProdRate;
    2218        44136 :         thermalPowerOutput = thermProdRate;
    2219        44136 :         break;
    2220              :     }
    2221        55416 :     case GeneratorType::CombTurbine: {
    2222              : 
    2223        55416 :         auto *thisCTE = CTElectricGenerator::CTGeneratorData::factory(state, name);
    2224              :         // dummy vars
    2225        55416 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2226        55416 :         Real64 tempLoad = myElecLoadRequest;
    2227              : 
    2228              :         // simulate
    2229        55416 :         thisCTE->simulate(state, L, FirstHVACIteration, tempLoad, runFlag);
    2230        55416 :         dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->InitCTGenerators(state, runFlag, FirstHVACIteration);
    2231        55416 :         dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->CalcCTGeneratorModel(state, runFlag, tempLoad, FirstHVACIteration);
    2232        55416 :         electProdRate = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->ElecPowerGenerated;
    2233        55416 :         electricityProd = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->ElecEnergyGenerated;
    2234        55416 :         thermProdRate = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->QTotalHeatRecovered;
    2235        55416 :         thermalProd = dynamic_cast<CTElectricGenerator::CTGeneratorData *>(thisCTE)->TotalHeatEnergyRec;
    2236        55416 :         electricPowerOutput = electProdRate;
    2237        55416 :         thermalPowerOutput = thermProdRate;
    2238        55416 :         break;
    2239              :     }
    2240       571944 :     case GeneratorType::PV: {
    2241       571944 :         Photovoltaics::SimPVGenerator(state, GeneratorType::PV, name, generatorIndex, runFlag, myElecLoadRequest);
    2242       571944 :         Photovoltaics::GetPVGeneratorResults(
    2243       571944 :             state, GeneratorType::PV, generatorIndex, dCElectProdRate, dCElectricityProd, thermProdRate, thermalProd);
    2244       571944 :         electricPowerOutput = dCElectProdRate;
    2245       571944 :         thermalPowerOutput = thermProdRate;
    2246       571944 :         break;
    2247              :     }
    2248         6588 :     case GeneratorType::PVWatts: {
    2249         6588 :         pvwattsGenerator->calc(state);
    2250         6588 :         pvwattsGenerator->getResults(dCElectProdRate, dCElectricityProd, thermProdRate, thermalProd);
    2251         6588 :         electricPowerOutput = dCElectProdRate;
    2252         6588 :         thermalPowerOutput = thermProdRate;
    2253         6588 :         break;
    2254              :     }
    2255        23433 :     case GeneratorType::FuelCell: {
    2256        23433 :         auto *thisFC = FuelCellElectricGenerator::FCDataStruct::factory(state, name);
    2257        23433 :         dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->SimFuelCellGenerator(state, runFlag, myElecLoadRequest, FirstHVACIteration);
    2258        23433 :         electProdRate = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.ACPowerGen;
    2259        23433 :         electricityProd = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.ACEnergyGen;
    2260        23433 :         thermProdRate = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.qHX;
    2261        23433 :         thermalProd = dynamic_cast<FuelCellElectricGenerator::FCDataStruct *>(thisFC)->Report.HXenergy;
    2262        23433 :         electricPowerOutput = electProdRate;
    2263        23433 :         thermalPowerOutput = thermProdRate;
    2264        23433 :         break;
    2265              :     }
    2266        17415 :     case GeneratorType::MicroCHP: {
    2267        17415 :         auto *thisMCHP = MicroCHPElectricGenerator::MicroCHPDataStruct::factory(state, name);
    2268              : 
    2269              :         // simulate
    2270        17415 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->InitMicroCHPNoNormalizeGenerators(state);
    2271              : 
    2272        17415 :         if (!state.dataPlnt->PlantFirstSizeCompleted) {
    2273         2087 :             break;
    2274              :         }
    2275              : 
    2276        15328 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->CalcMicroCHPNoNormalizeGeneratorModel(
    2277              :             state, runFlag, false, myElecLoadRequest, DataPrecisionGlobals::constant_zero);
    2278        15328 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->CalcUpdateHeatRecovery(state);
    2279        15328 :         dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->UpdateMicroCHPGeneratorRecords(state);
    2280              : 
    2281        15328 :         electProdRate = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.ACPowerGen;
    2282        15328 :         electricityProd = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.ACEnergyGen;
    2283        15328 :         thermProdRate = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.QdotHR;
    2284        15328 :         thermalProd = dynamic_cast<MicroCHPElectricGenerator::MicroCHPDataStruct *>(thisMCHP)->A42Model.TotalHeatEnergyRec;
    2285        15328 :         electricPowerOutput = electProdRate;
    2286        15328 :         thermalPowerOutput = thermProdRate;
    2287        15328 :         break;
    2288              :     }
    2289        51343 :     case GeneratorType::Microturbine: {
    2290        51343 :         auto *thisMTG = MicroturbineElectricGenerator::MTGeneratorSpecs::factory(state, name);
    2291              : 
    2292              :         // dummy vars
    2293        51343 :         PlantLocation L(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    2294        51343 :         Real64 tempLoad = myElecLoadRequest;
    2295              : 
    2296              :         // simulate
    2297        51343 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->InitMTGenerators(state, runFlag, tempLoad, FirstHVACIteration);
    2298        51343 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->CalcMTGeneratorModel(state, runFlag, tempLoad);
    2299        51343 :         dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->UpdateMTGeneratorRecords(state);
    2300        51343 :         electProdRate = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->ElecPowerGenerated;
    2301        51343 :         electricityProd = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->EnergyGen;
    2302        51343 :         thermProdRate = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->QHeatRecovered;
    2303        51343 :         thermalProd = dynamic_cast<MicroturbineElectricGenerator::MTGeneratorSpecs *>(thisMTG)->ExhaustEnergyRec;
    2304        51343 :         electricPowerOutput = electProdRate;
    2305        51343 :         thermalPowerOutput = thermProdRate;
    2306        51343 :         break;
    2307              :     }
    2308        16236 :     case GeneratorType::WindTurbine: {
    2309        16236 :         WindTurbine::SimWindTurbine(state, GeneratorType::WindTurbine, name, generatorIndex, runFlag, myElecLoadRequest);
    2310        16236 :         WindTurbine::GetWTGeneratorResults(
    2311        16236 :             state, GeneratorType::WindTurbine, generatorIndex, electProdRate, electricityProd, thermProdRate, thermalProd);
    2312        16236 :         electricPowerOutput = electProdRate;
    2313        16236 :         thermalPowerOutput = thermProdRate;
    2314        16236 :         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       786511 :     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       786511 : }
    2340              : 
    2341           12 : DCtoACInverter::DCtoACInverter(EnergyPlusData &state, std::string const &objectName)
    2342           12 :     : aCPowerOut_(0.0), aCEnergyOut_(0.0), efficiency_(0.0), dCPowerIn_(0.0), dCEnergyIn_(0.0), conversionLossPower_(0.0), conversionLossEnergy_(0.0),
    2343           12 :       conversionLossEnergyDecrement_(0.0), thermLossRate_(0.0), thermLossEnergy_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0), ancillACuseRate_(0.0),
    2344           12 :       ancillACuseEnergy_(0.0), modelType_(InverterModelType::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    2345           12 :       zoneRadFract_(0.0), nominalVoltage_(0.0), nomVoltEfficiencyARR_(6, 0.0), ratedPower_(0.0), minPower_(0.0), maxPower_(0.0), minEfficiency_(0.0),
    2346           12 :       maxEfficiency_(0.0), standbyPower_(0.0)
    2347              : {
    2348              :     // initialize
    2349           12 :     nomVoltEfficiencyARR_.resize(6, 0.0);
    2350              : 
    2351              :     static constexpr std::string_view routineName = "DCtoACInverter constructor ";
    2352              : 
    2353           12 :     auto &s_ipsc = state.dataIPShortCut;
    2354              : 
    2355           12 :     bool errorsFound = false;
    2356              :     // if/when add object class name to input object this can be simplified. for now search all possible types
    2357           12 :     bool foundInverter = false;
    2358           12 :     int testInvertIndex = 0;
    2359           12 :     int invertIDFObjectNum = 0;
    2360              : 
    2361           12 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:LookUpTable", objectName);
    2362           12 :     if (testInvertIndex > 0) {
    2363            9 :         foundInverter = true;
    2364            9 :         invertIDFObjectNum = testInvertIndex;
    2365            9 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:LookUpTable";
    2366            9 :         modelType_ = InverterModelType::CECLookUpTableModel;
    2367              :     }
    2368           12 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:FunctionOfPower", objectName);
    2369           12 :     if (testInvertIndex > 0) {
    2370            1 :         foundInverter = true;
    2371            1 :         invertIDFObjectNum = testInvertIndex;
    2372            1 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:FunctionOfPower";
    2373            1 :         modelType_ = InverterModelType::CurveFuncOfPower;
    2374              :     }
    2375           12 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:Simple", objectName);
    2376           12 :     if (testInvertIndex > 0) {
    2377            1 :         foundInverter = true;
    2378            1 :         invertIDFObjectNum = testInvertIndex;
    2379            1 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:Simple";
    2380            1 :         modelType_ = InverterModelType::SimpleConstantEff;
    2381              :     }
    2382           12 :     testInvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Inverter:PVWatts", objectName);
    2383           12 :     if (testInvertIndex > 0) {
    2384            1 :         foundInverter = true;
    2385            1 :         invertIDFObjectNum = testInvertIndex;
    2386            1 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Inverter:PVWatts";
    2387            1 :         modelType_ = InverterModelType::PVWatts;
    2388              :     }
    2389              : 
    2390           12 :     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           24 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    2396           12 :                                                                  s_ipsc->cCurrentModuleObject,
    2397              :                                                                  invertIDFObjectNum,
    2398           12 :                                                                  s_ipsc->cAlphaArgs,
    2399              :                                                                  NumAlphas,
    2400           12 :                                                                  s_ipsc->rNumericArgs,
    2401              :                                                                  NumNums,
    2402              :                                                                  IOStat,
    2403           12 :                                                                  s_ipsc->lNumericFieldBlanks,
    2404           12 :                                                                  s_ipsc->lAlphaFieldBlanks,
    2405           12 :                                                                  s_ipsc->cAlphaFieldNames,
    2406           12 :                                                                  s_ipsc->cNumericFieldNames);
    2407              : 
    2408           12 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    2409           12 :         name_ = s_ipsc->cAlphaArgs(1);
    2410              :         // how to verify names are unique across objects? add to GlobalNames?
    2411              : 
    2412           12 :         if (modelType_ == InverterModelType::PVWatts) {
    2413            1 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2414            1 :             zoneNum_ = 0;
    2415            1 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2416            1 :             zoneRadFract_ = 0;
    2417           11 :         } else if (s_ipsc->lAlphaFieldBlanks(2)) {
    2418            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2419              :         } else {
    2420           11 :             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           11 :             if (s_ipsc->lAlphaFieldBlanks(3)) {
    2426            9 :                 heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    2427            2 :             } 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            2 :                 heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    2434              :             }
    2435           11 :             zoneRadFract_ = s_ipsc->rNumericArgs(1);
    2436              :         }
    2437              : 
    2438              :         // now the input objects differ depending on class type
    2439           12 :         switch (modelType_) {
    2440            9 :         case InverterModelType::CECLookUpTableModel: {
    2441            9 :             ratedPower_ = s_ipsc->rNumericArgs(2);
    2442            9 :             standbyPower_ = s_ipsc->rNumericArgs(3);
    2443              : 
    2444            9 :             nominalVoltage_ = s_ipsc->rNumericArgs(4);
    2445            9 :             nomVoltEfficiencyARR_[0] = s_ipsc->rNumericArgs(5);
    2446            9 :             nomVoltEfficiencyARR_[1] = s_ipsc->rNumericArgs(6);
    2447            9 :             nomVoltEfficiencyARR_[2] = s_ipsc->rNumericArgs(7);
    2448            9 :             nomVoltEfficiencyARR_[3] = s_ipsc->rNumericArgs(8);
    2449            9 :             nomVoltEfficiencyARR_[4] = s_ipsc->rNumericArgs(9);
    2450            9 :             nomVoltEfficiencyARR_[5] = s_ipsc->rNumericArgs(10);
    2451            9 :             break;
    2452              :         }
    2453            1 :         case InverterModelType::CurveFuncOfPower: {
    2454            1 :             if (s_ipsc->lAlphaFieldBlanks(4)) {
    2455            0 :                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
    2456            0 :                 errorsFound = true;
    2457            1 :             } 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            1 :             ratedPower_ = s_ipsc->rNumericArgs(2);
    2463            1 :             minEfficiency_ = s_ipsc->rNumericArgs(3);
    2464            1 :             maxEfficiency_ = s_ipsc->rNumericArgs(4);
    2465            1 :             minPower_ = s_ipsc->rNumericArgs(5);
    2466            1 :             maxPower_ = s_ipsc->rNumericArgs(6);
    2467            1 :             standbyPower_ = s_ipsc->rNumericArgs(7);
    2468            1 :             break;
    2469              :         }
    2470            1 :         case InverterModelType::SimpleConstantEff: {
    2471            1 :             efficiency_ = s_ipsc->rNumericArgs(2);
    2472            1 :             break;
    2473              :         }
    2474            0 :         case InverterModelType::Invalid: {
    2475              :             // do nothing
    2476            0 :             break;
    2477              :         }
    2478            1 :         case InverterModelType::PVWatts: {
    2479            1 :             pvWattsDCtoACSizeRatio_ = s_ipsc->rNumericArgs(1);
    2480            1 :             pvWattsInverterEfficiency_ = s_ipsc->rNumericArgs(2);
    2481            1 :             break;
    2482              :         }
    2483            0 :         default:
    2484            0 :             assert(false);
    2485              :         } // end switch modelType
    2486              : 
    2487           24 :         SetupOutputVariable(state,
    2488              :                             "Inverter DC to AC Efficiency",
    2489              :                             Constant::Units::None,
    2490           12 :                             efficiency_,
    2491              :                             OutputProcessor::TimeStepType::System,
    2492              :                             OutputProcessor::StoreType::Average,
    2493           12 :                             name_);
    2494           24 :         SetupOutputVariable(state,
    2495              :                             "Inverter DC Input Electricity Rate",
    2496              :                             Constant::Units::W,
    2497           12 :                             dCPowerIn_,
    2498              :                             OutputProcessor::TimeStepType::System,
    2499              :                             OutputProcessor::StoreType::Average,
    2500           12 :                             name_);
    2501           24 :         SetupOutputVariable(state,
    2502              :                             "Inverter DC Input Electricity Energy",
    2503              :                             Constant::Units::J,
    2504           12 :                             dCEnergyIn_,
    2505              :                             OutputProcessor::TimeStepType::System,
    2506              :                             OutputProcessor::StoreType::Sum,
    2507           12 :                             name_);
    2508           24 :         SetupOutputVariable(state,
    2509              :                             "Inverter AC Output Electricity Rate",
    2510              :                             Constant::Units::W,
    2511           12 :                             aCPowerOut_,
    2512              :                             OutputProcessor::TimeStepType::System,
    2513              :                             OutputProcessor::StoreType::Average,
    2514           12 :                             name_);
    2515           24 :         SetupOutputVariable(state,
    2516              :                             "Inverter AC Output Electricity Energy",
    2517              :                             Constant::Units::J,
    2518           12 :                             aCEnergyOut_,
    2519              :                             OutputProcessor::TimeStepType::System,
    2520              :                             OutputProcessor::StoreType::Sum,
    2521           12 :                             name_);
    2522           24 :         SetupOutputVariable(state,
    2523              :                             "Inverter Conversion Loss Power",
    2524              :                             Constant::Units::W,
    2525           12 :                             conversionLossPower_,
    2526              :                             OutputProcessor::TimeStepType::System,
    2527              :                             OutputProcessor::StoreType::Average,
    2528           12 :                             name_);
    2529           24 :         SetupOutputVariable(state,
    2530              :                             "Inverter Conversion Loss Energy",
    2531              :                             Constant::Units::J,
    2532           12 :                             conversionLossEnergy_,
    2533              :                             OutputProcessor::TimeStepType::System,
    2534              :                             OutputProcessor::StoreType::Sum,
    2535           12 :                             name_);
    2536           24 :         SetupOutputVariable(state,
    2537              :                             "Inverter Conversion Loss Decrement Energy",
    2538              :                             Constant::Units::J,
    2539           12 :                             conversionLossEnergyDecrement_,
    2540              :                             OutputProcessor::TimeStepType::System,
    2541              :                             OutputProcessor::StoreType::Sum,
    2542           12 :                             name_,
    2543              :                             Constant::eResource::ElectricityProduced,
    2544              :                             OutputProcessor::Group::Plant,
    2545              :                             OutputProcessor::EndUseCat::PowerConversion);
    2546           24 :         SetupOutputVariable(state,
    2547              :                             "Inverter Thermal Loss Rate",
    2548              :                             Constant::Units::W,
    2549           12 :                             thermLossRate_,
    2550              :                             OutputProcessor::TimeStepType::System,
    2551              :                             OutputProcessor::StoreType::Average,
    2552           12 :                             name_);
    2553           24 :         SetupOutputVariable(state,
    2554              :                             "Inverter Thermal Loss Energy",
    2555              :                             Constant::Units::J,
    2556           12 :                             thermLossEnergy_,
    2557              :                             OutputProcessor::TimeStepType::System,
    2558              :                             OutputProcessor::StoreType::Sum,
    2559           12 :                             name_);
    2560           24 :         SetupOutputVariable(state,
    2561              :                             "Inverter Ancillary AC Electricity Rate",
    2562              :                             Constant::Units::W,
    2563           12 :                             ancillACuseRate_,
    2564              :                             OutputProcessor::TimeStepType::System,
    2565              :                             OutputProcessor::StoreType::Average,
    2566           12 :                             name_);
    2567           24 :         SetupOutputVariable(state,
    2568              :                             "Inverter Ancillary AC Electricity Energy",
    2569              :                             Constant::Units::J,
    2570           12 :                             ancillACuseEnergy_,
    2571              :                             OutputProcessor::TimeStepType::System,
    2572              :                             OutputProcessor::StoreType::Sum,
    2573           12 :                             name_,
    2574              :                             Constant::eResource::Electricity,
    2575              :                             OutputProcessor::Group::Plant, // called cogeneration for end use table
    2576              :                             OutputProcessor::EndUseCat::Cogeneration,
    2577              :                             "DCtoACInverter Ancillary");
    2578           12 :         if (zoneNum_ > 0) {
    2579            2 :             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            2 :             case InverterModelType::CECLookUpTableModel: {
    2596            2 :                 SetupZoneInternalGain(state,
    2597              :                                       zoneNum_,
    2598              :                                       name_,
    2599              :                                       DataHeatBalance::IntGainType::ElectricLoadCenterInverterLookUpTable,
    2600              :                                       &qdotConvZone_,
    2601              :                                       nullptr,
    2602              :                                       &qdotRadZone_);
    2603            2 :                 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           12 :     if (errorsFound) {
    2622            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    2623              :     }
    2624           12 : }
    2625              : 
    2626          104 : void DCtoACInverter::reinitAtBeginEnvironment()
    2627              : {
    2628          104 :     ancillACuseRate_ = 0.0;
    2629          104 :     ancillACuseEnergy_ = 0.0;
    2630          104 :     qdotConvZone_ = 0.0;
    2631          104 :     qdotRadZone_ = 0.0;
    2632          104 : }
    2633              : 
    2634           92 : void DCtoACInverter::reinitZoneGainsAtBeginEnvironment()
    2635              : {
    2636           92 :     qdotConvZone_ = 0.0;
    2637           92 :     qdotRadZone_ = 0.0;
    2638           92 : }
    2639              : 
    2640            1 : void DCtoACInverter::setPVWattsDCCapacity(EnergyPlusData &state, const Real64 dcCapacity)
    2641              : {
    2642            1 :     if (modelType_ != InverterModelType::PVWatts) {
    2643            0 :         ShowFatalError(state, "Setting the DC Capacity for the inverter only works with PVWatts Inverters.");
    2644              :     }
    2645            1 :     ratedPower_ = dcCapacity / pvWattsDCtoACSizeRatio_;
    2646            1 : }
    2647              : 
    2648            0 : Real64 DCtoACInverter::pvWattsDCCapacity()
    2649              : {
    2650            0 :     return ratedPower_ * pvWattsDCtoACSizeRatio_;
    2651              : }
    2652              : 
    2653            3 : Real64 DCtoACInverter::pvWattsInverterEfficiency()
    2654              : {
    2655            3 :     return pvWattsInverterEfficiency_;
    2656              : }
    2657              : 
    2658            3 : Real64 DCtoACInverter::pvWattsDCtoACSizeRatio()
    2659              : {
    2660            3 :     return pvWattsDCtoACSizeRatio_;
    2661              : }
    2662              : 
    2663       292414 : Real64 DCtoACInverter::aCPowerOut() const
    2664              : {
    2665       292414 :     return aCPowerOut_;
    2666              : }
    2667              : 
    2668           12 : DCtoACInverter::InverterModelType DCtoACInverter::modelType() const
    2669              : {
    2670           12 :     return modelType_;
    2671              : }
    2672              : 
    2673           12 : std::string const &DCtoACInverter::name() const
    2674              : {
    2675           12 :     return name_;
    2676              : }
    2677              : 
    2678       130975 : 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       130975 :     if (efficiency_ > 0.0) {
    2684       130966 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2685              :     } else {
    2686            9 :         dCPowerIn_ = powerOutOfInverter;
    2687            9 :         calcEfficiency(state);
    2688            9 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2689              :     }
    2690              : 
    2691       130975 :     calcEfficiency(state);
    2692              :     // one more update is close enough.
    2693       130975 :     if (efficiency_ > 0.0) {
    2694       130975 :         dCPowerIn_ = powerOutOfInverter / efficiency_;
    2695              :     }
    2696       130975 :     calcEfficiency(state);
    2697       130975 :     return (1.0 - efficiency_) * dCPowerIn_;
    2698              : }
    2699              : 
    2700       408166 : void DCtoACInverter::calcEfficiency(EnergyPlusData &state)
    2701              : {
    2702       408166 :     switch (modelType_) {
    2703       392934 :     case InverterModelType::CECLookUpTableModel: {
    2704              :         // we don't model voltage, so use nominal voltage
    2705       392934 :         Real64 normalizedPower = dCPowerIn_ / ratedPower_;
    2706              : 
    2707              :         // get efficiency
    2708       392934 :         if (normalizedPower <= 0.1) {
    2709              :             // extrapolate or fix at 10% value? fix it for now
    2710       228299 :             efficiency_ = nomVoltEfficiencyARR_[0];
    2711       164635 :         } else if ((normalizedPower > 0.1) && (normalizedPower < 0.20)) {
    2712         6088 :             efficiency_ = nomVoltEfficiencyARR_[0] + ((normalizedPower - 0.1) / (0.2 - 0.1)) * (nomVoltEfficiencyARR_[1] - nomVoltEfficiencyARR_[0]);
    2713       158547 :         } else if (normalizedPower == 0.2) {
    2714            0 :             efficiency_ = nomVoltEfficiencyARR_[1];
    2715       158547 :         } else if ((normalizedPower > 0.2) && (normalizedPower < 0.30)) {
    2716         5175 :             efficiency_ = nomVoltEfficiencyARR_[1] + ((normalizedPower - 0.2) / (0.3 - 0.2)) * (nomVoltEfficiencyARR_[2] - nomVoltEfficiencyARR_[1]);
    2717       153372 :         } else if (normalizedPower == 0.3) {
    2718            0 :             efficiency_ = nomVoltEfficiencyARR_[2];
    2719       153372 :         } else if ((normalizedPower > 0.3) && (normalizedPower < 0.50)) {
    2720         7156 :             efficiency_ = nomVoltEfficiencyARR_[2] + ((normalizedPower - 0.3) / (0.5 - 0.3)) * (nomVoltEfficiencyARR_[3] - nomVoltEfficiencyARR_[2]);
    2721       146216 :         } else if (normalizedPower == 0.5) {
    2722            0 :             efficiency_ = nomVoltEfficiencyARR_[3];
    2723       146216 :         } else if ((normalizedPower > 0.5) && (normalizedPower < 0.75)) {
    2724         7536 :             efficiency_ = nomVoltEfficiencyARR_[3] + ((normalizedPower - 0.5) / (0.75 - 0.5)) * (nomVoltEfficiencyARR_[4] - nomVoltEfficiencyARR_[3]);
    2725       138680 :         } else if (normalizedPower == 0.75) {
    2726            0 :             efficiency_ = nomVoltEfficiencyARR_[4];
    2727       138680 :         } else if ((normalizedPower > 0.75) && (normalizedPower < 1.0)) {
    2728         4483 :             efficiency_ =
    2729         4483 :                 nomVoltEfficiencyARR_[4] + ((normalizedPower - 0.75) / (1.0 - 0.75)) * (nomVoltEfficiencyARR_[5] - nomVoltEfficiencyARR_[4]);
    2730       134197 :         } else if (normalizedPower >= 1.0) {
    2731       134197 :             efficiency_ = nomVoltEfficiencyARR_[5];
    2732              :         } else {
    2733            0 :             assert(false);
    2734              :         }
    2735              : 
    2736       392934 :         efficiency_ = max(efficiency_, 0.0);
    2737       392934 :         efficiency_ = min(efficiency_, 1.0);
    2738       392934 :     } break;
    2739              : 
    2740         8293 :     case InverterModelType::CurveFuncOfPower: {
    2741         8293 :         Real64 normalizedPower = dCPowerIn_ / ratedPower_;
    2742         8293 :         efficiency_ = effCurve_->value(state, normalizedPower);
    2743         8293 :         efficiency_ = max(efficiency_, minEfficiency_);
    2744         8293 :         efficiency_ = min(efficiency_, maxEfficiency_);
    2745         8293 :     } break;
    2746              : 
    2747         2196 :     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         2196 :         Real64 constexpr etaref = 0.9637;
    2752         2196 :         Real64 constexpr A = -0.0162;
    2753         2196 :         Real64 constexpr B = -0.0059;
    2754         2196 :         Real64 constexpr C = 0.9858;
    2755         2196 :         Real64 const pdc0 = ratedPower_ / pvWattsInverterEfficiency_;
    2756         2196 :         Real64 const plr = dCPowerIn_ / pdc0;
    2757         2196 :         Real64 ac = 0;
    2758              : 
    2759         2196 :         if (plr > 0) {
    2760              :             // normal operation
    2761          990 :             Real64 eta = (A * plr + B / plr + C) * pvWattsInverterEfficiency_ / etaref;
    2762          990 :             ac = dCPowerIn_ * eta;
    2763          990 :             if (ac > ratedPower_) {
    2764              :                 // clipping
    2765            0 :                 ac = ratedPower_;
    2766              :             }
    2767              :             // make sure no negative AC values (no parasitic nighttime losses calculated)
    2768          990 :             if (ac < 0) {
    2769           90 :                 ac = 0;
    2770              :             }
    2771          990 :             efficiency_ = ac / dCPowerIn_;
    2772              :         } else {
    2773         1206 :             efficiency_ = 1.0; // Set to a non-zero reasonable value (to avoid divide by zero error)
    2774              :         }
    2775         2196 :         break;
    2776              :     }
    2777         4743 :     case InverterModelType::SimpleConstantEff:
    2778              :     case InverterModelType::Invalid: {
    2779              :         // do nothing
    2780         4743 :         break;
    2781              :     }
    2782            0 :     default:
    2783            0 :         assert(false);
    2784              :     } // end switch
    2785       408166 : }
    2786              : 
    2787       146207 : void DCtoACInverter::simulate(EnergyPlusData &state, Real64 const powerIntoInverter)
    2788              : {
    2789       146207 :     dCPowerIn_ = powerIntoInverter;
    2790       146207 :     dCEnergyIn_ = dCPowerIn_ * (state.dataHVACGlobal->TimeStepSysSec);
    2791              :     // check availability schedule
    2792       146207 :     if (availSched_->getCurrentVal() > 0.0) {
    2793              : 
    2794              :         // now calculate Inverter based on model type
    2795       146207 :         calcEfficiency(state);
    2796       146207 :         aCPowerOut_ = efficiency_ * dCPowerIn_;
    2797       146207 :         aCEnergyOut_ = aCPowerOut_ * (state.dataHVACGlobal->TimeStepSysSec);
    2798              : 
    2799       146207 :         if (aCPowerOut_ == 0.0) {
    2800        64540 :             ancillACuseEnergy_ = standbyPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    2801        64540 :             ancillACuseRate_ = standbyPower_;
    2802              :         } else {
    2803        81667 :             ancillACuseRate_ = 0.0;
    2804        81667 :             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       146207 :     conversionLossPower_ = dCPowerIn_ - aCPowerOut_;
    2815       146207 :     conversionLossEnergy_ = conversionLossPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    2816       146207 :     conversionLossEnergyDecrement_ = -1.0 * conversionLossEnergy_;
    2817       146207 :     thermLossRate_ = dCPowerIn_ - aCPowerOut_ + ancillACuseRate_;
    2818       146207 :     thermLossEnergy_ = thermLossRate_ * (state.dataHVACGlobal->TimeStepSysSec);
    2819       146207 :     qdotConvZone_ = thermLossRate_ * (1.0 - zoneRadFract_);
    2820       146207 :     qdotRadZone_ = thermLossRate_ * zoneRadFract_;
    2821       146207 : }
    2822              : 
    2823            4 : ACtoDCConverter::ACtoDCConverter(EnergyPlusData &state, std::string const &objectName)
    2824            4 :     : efficiency_(0.0), aCPowerIn_(0.0), aCEnergyIn_(0.0), dCPowerOut_(0.0), dCEnergyOut_(0.0), conversionLossPower_(0.0), conversionLossEnergy_(0.0),
    2825            4 :       conversionLossEnergyDecrement_(0.0), thermLossRate_(0.0), thermLossEnergy_(0.0), qdotConvZone_(0.0), qdotRadZone_(0.0), ancillACuseRate_(0.0),
    2826            4 :       ancillACuseEnergy_(0.0), modelType_(ConverterModelType::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    2827            4 :       zoneRadFract_(0.0), // radiative fraction for thermal losses to zone
    2828            4 :       standbyPower_(0.0), maxPower_(0.0)
    2829              : {
    2830              : 
    2831              :     static constexpr std::string_view routineName = "ACtoDCConverter constructor ";
    2832              : 
    2833            4 :     auto &s_ipsc = state.dataIPShortCut;
    2834              : 
    2835            4 :     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            4 :     int testConvertIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Storage:Converter", objectName);
    2839              : 
    2840            4 :     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            4 :         s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Storage:Converter";
    2845              : 
    2846            8 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    2847            4 :                                                                  s_ipsc->cCurrentModuleObject,
    2848              :                                                                  testConvertIndex,
    2849            4 :                                                                  s_ipsc->cAlphaArgs,
    2850              :                                                                  NumAlphas,
    2851            4 :                                                                  s_ipsc->rNumericArgs,
    2852              :                                                                  NumNums,
    2853              :                                                                  IOStat,
    2854            4 :                                                                  s_ipsc->lNumericFieldBlanks,
    2855            4 :                                                                  s_ipsc->lAlphaFieldBlanks,
    2856            4 :                                                                  s_ipsc->cAlphaFieldNames,
    2857            4 :                                                                  s_ipsc->cNumericFieldNames);
    2858              : 
    2859            4 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    2860              : 
    2861            4 :         name_ = s_ipsc->cAlphaArgs(1);
    2862              :         // need a new general approach for verify names are unique across objects,  next gen GlobalNames
    2863              : 
    2864            4 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    2865            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    2866            4 :         } 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            4 :         modelType_ = static_cast<ConverterModelType>(getEnumValue(converterModelTypeNamesUC, s_ipsc->cAlphaArgs(3)));
    2872            4 :         if (modelType_ == ConverterModelType::Invalid) {
    2873            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    2874            0 :             errorsFound = true;
    2875              :         }
    2876              : 
    2877            4 :         switch (modelType_) {
    2878            4 :         case ConverterModelType::SimpleConstantEff: {
    2879            4 :             efficiency_ = s_ipsc->rNumericArgs(1);
    2880            4 :         } 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            4 :         standbyPower_ = s_ipsc->rNumericArgs(3);
    2903              : 
    2904            4 :         if (s_ipsc->lAlphaFieldBlanks(5)) {
    2905            4 :             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            4 :         zoneRadFract_ = s_ipsc->rNumericArgs(4);
    2916              : 
    2917            8 :         SetupOutputVariable(state,
    2918              :                             "Converter AC to DC Efficiency",
    2919              :                             Constant::Units::None,
    2920            4 :                             efficiency_,
    2921              :                             OutputProcessor::TimeStepType::System,
    2922              :                             OutputProcessor::StoreType::Average,
    2923            4 :                             name_);
    2924            8 :         SetupOutputVariable(state,
    2925              :                             "Converter AC Input Electricity Rate",
    2926              :                             Constant::Units::W,
    2927            4 :                             aCPowerIn_,
    2928              :                             OutputProcessor::TimeStepType::System,
    2929              :                             OutputProcessor::StoreType::Average,
    2930            4 :                             name_);
    2931            8 :         SetupOutputVariable(state,
    2932              :                             "Converter AC Input Electricity Energy",
    2933              :                             Constant::Units::J,
    2934            4 :                             aCEnergyIn_,
    2935              :                             OutputProcessor::TimeStepType::System,
    2936              :                             OutputProcessor::StoreType::Sum,
    2937            4 :                             name_);
    2938            8 :         SetupOutputVariable(state,
    2939              :                             "Converter DC Output Electricity Rate",
    2940              :                             Constant::Units::W,
    2941            4 :                             dCPowerOut_,
    2942              :                             OutputProcessor::TimeStepType::System,
    2943              :                             OutputProcessor::StoreType::Average,
    2944            4 :                             name_);
    2945            8 :         SetupOutputVariable(state,
    2946              :                             "Converter DC Output Electricity Energy",
    2947              :                             Constant::Units::J,
    2948            4 :                             dCEnergyOut_,
    2949              :                             OutputProcessor::TimeStepType::System,
    2950              :                             OutputProcessor::StoreType::Sum,
    2951            4 :                             name_);
    2952            8 :         SetupOutputVariable(state,
    2953              :                             "Converter Electricity Loss Rate",
    2954              :                             Constant::Units::W,
    2955            4 :                             conversionLossPower_,
    2956              :                             OutputProcessor::TimeStepType::System,
    2957              :                             OutputProcessor::StoreType::Average,
    2958            4 :                             name_);
    2959            8 :         SetupOutputVariable(state,
    2960              :                             "Converter Electricity Loss Energy",
    2961              :                             Constant::Units::J,
    2962            4 :                             conversionLossEnergy_,
    2963              :                             OutputProcessor::TimeStepType::System,
    2964              :                             OutputProcessor::StoreType::Sum,
    2965            4 :                             name_);
    2966            8 :         SetupOutputVariable(state,
    2967              :                             "Converter Electricity Loss Decrement Energy",
    2968              :                             Constant::Units::J,
    2969            4 :                             conversionLossEnergyDecrement_,
    2970              :                             OutputProcessor::TimeStepType::System,
    2971              :                             OutputProcessor::StoreType::Sum,
    2972            4 :                             name_,
    2973              :                             Constant::eResource::ElectricityProduced,
    2974              :                             OutputProcessor::Group::Plant,
    2975              :                             OutputProcessor::EndUseCat::PowerConversion);
    2976            8 :         SetupOutputVariable(state,
    2977              :                             "Converter Thermal Loss Rate",
    2978              :                             Constant::Units::W,
    2979            4 :                             thermLossRate_,
    2980              :                             OutputProcessor::TimeStepType::System,
    2981              :                             OutputProcessor::StoreType::Average,
    2982            4 :                             name_);
    2983            8 :         SetupOutputVariable(state,
    2984              :                             "Converter Thermal Loss Energy",
    2985              :                             Constant::Units::J,
    2986            4 :                             thermLossEnergy_,
    2987              :                             OutputProcessor::TimeStepType::System,
    2988              :                             OutputProcessor::StoreType::Sum,
    2989            4 :                             name_);
    2990            8 :         SetupOutputVariable(state,
    2991              :                             "Converter Ancillary AC Electricity Rate",
    2992              :                             Constant::Units::W,
    2993            4 :                             ancillACuseRate_,
    2994              :                             OutputProcessor::TimeStepType::System,
    2995              :                             OutputProcessor::StoreType::Average,
    2996            4 :                             name_);
    2997            8 :         SetupOutputVariable(state,
    2998              :                             "Converter Ancillary AC Electricity Energy",
    2999              :                             Constant::Units::J,
    3000            4 :                             ancillACuseEnergy_,
    3001              :                             OutputProcessor::TimeStepType::System,
    3002              :                             OutputProcessor::StoreType::Sum,
    3003            4 :                             name_,
    3004              :                             Constant::eResource::Electricity,
    3005              :                             OutputProcessor::Group::Plant, // called cogeneration for end use table
    3006              :                             OutputProcessor::EndUseCat::Cogeneration,
    3007              :                             "ACtoDCConverter Ancillary");
    3008            4 :         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            4 :     if (errorsFound) {
    3018            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    3019              :     }
    3020            4 : }
    3021              : 
    3022           36 : void ACtoDCConverter::reinitAtBeginEnvironment()
    3023              : {
    3024           36 :     ancillACuseRate_ = 0.0;
    3025           36 :     ancillACuseEnergy_ = 0.0;
    3026           36 :     qdotConvZone_ = 0.0;
    3027           36 :     qdotRadZone_ = 0.0;
    3028           36 : }
    3029              : 
    3030           32 : void ACtoDCConverter::reinitZoneGainsAtBeginEnvironment()
    3031              : {
    3032           32 :     qdotConvZone_ = 0.0;
    3033           32 :     qdotRadZone_ = 0.0;
    3034           32 : }
    3035              : 
    3036       138744 : Real64 ACtoDCConverter::aCPowerIn() const
    3037              : {
    3038       138744 :     return aCPowerIn_;
    3039              : }
    3040              : 
    3041        69372 : Real64 ACtoDCConverter::getLossRateForInputPower(EnergyPlusData &state, Real64 const powerIntoConverter)
    3042              : {
    3043        69372 :     aCPowerIn_ = powerIntoConverter;
    3044        69372 :     calcEfficiency(state);
    3045        69372 :     return (1.0 - efficiency_) * aCPowerIn_;
    3046              : }
    3047              : 
    3048            4 : std::string const &ACtoDCConverter::name() const
    3049              : {
    3050            4 :     return name_;
    3051              : }
    3052              : 
    3053       208116 : void ACtoDCConverter::calcEfficiency(EnergyPlusData &state)
    3054              : {
    3055       208116 :     switch (modelType_) {
    3056       208116 :     case ConverterModelType::Invalid:
    3057              :     case ConverterModelType::SimpleConstantEff: {
    3058       208116 :         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       208116 : }
    3069              : 
    3070        69372 : 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        69372 :     if (availSched_->getCurrentVal() > 0.0) {
    3076              : 
    3077        69372 :         aCPowerIn_ = powerOutFromConverter / efficiency_;
    3078        69372 :         calcEfficiency(state), aCPowerIn_ = powerOutFromConverter / efficiency_;
    3079        69372 :         calcEfficiency(state),
    3080              : 
    3081        69372 :             dCPowerOut_ = aCPowerIn_ * efficiency_;
    3082              : 
    3083        69372 :         if (dCPowerOut_ == 0.0) {
    3084        42135 :             ancillACuseEnergy_ = standbyPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    3085        42135 :             ancillACuseRate_ = standbyPower_;
    3086              :         } else {
    3087        27237 :             ancillACuseRate_ = 0.0;
    3088        27237 :             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        69372 :     aCEnergyIn_ = aCPowerIn_ * (state.dataHVACGlobal->TimeStepSysSec);
    3100        69372 :     dCEnergyOut_ = dCPowerOut_ * (state.dataHVACGlobal->TimeStepSysSec);
    3101        69372 :     conversionLossPower_ = aCPowerIn_ - dCPowerOut_;
    3102        69372 :     conversionLossEnergy_ = conversionLossPower_ * (state.dataHVACGlobal->TimeStepSysSec);
    3103        69372 :     conversionLossEnergyDecrement_ = -1.0 * conversionLossEnergy_;
    3104        69372 :     thermLossRate_ = aCPowerIn_ - dCPowerOut_ + ancillACuseRate_;
    3105        69372 :     thermLossEnergy_ = thermLossRate_ * (state.dataHVACGlobal->TimeStepSysSec);
    3106        69372 :     qdotConvZone_ = thermLossRate_ * (1.0 - zoneRadFract_);
    3107        69372 :     qdotRadZone_ = thermLossRate_ * zoneRadFract_;
    3108        69372 : }
    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           12 :     for (auto &item : storageTypes) {
    3142           12 :         int testStorageIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, item.first, objectName);
    3143           12 :         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            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    3176            9 :         } 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            7 :             heatLossesDestination_ = ThermalLossDestination::LostToOutside;
    3183            2 :         } 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            2 :             heatLossesDestination_ = ThermalLossDestination::ZoneGains;
    3190              :         }
    3191              : 
    3192            9 :         zoneRadFract_ = s_ipsc->rNumericArgs(1);
    3193              : 
    3194            9 :         switch (storageModelMode_) {
    3195              : 
    3196            7 :         case StorageModelType::SimpleBucketStorage: {
    3197            7 :             energeticEfficCharge_ = checkUserEfficiencyInput(state, s_ipsc->rNumericArgs(2), true, name_, errorsFound);
    3198            7 :             energeticEfficDischarge_ = checkUserEfficiencyInput(state, s_ipsc->rNumericArgs(3), false, name_, errorsFound);
    3199            7 :             maxEnergyCapacity_ = s_ipsc->rNumericArgs(4);
    3200            7 :             maxPowerDraw_ = s_ipsc->rNumericArgs(5);
    3201            7 :             maxPowerStore_ = s_ipsc->rNumericArgs(6);
    3202            7 :             startingEnergyStored_ = s_ipsc->rNumericArgs(7);
    3203           14 :             SetupOutputVariable(state,
    3204              :                                 "Electric Storage Simple Charge State",
    3205              :                                 Constant::Units::J,
    3206            7 :                                 electEnergyinStorage_,
    3207              :                                 OutputProcessor::TimeStepType::System,
    3208              :                                 OutputProcessor::StoreType::Average,
    3209            7 :                                 name_); // issue #4921
    3210            7 :             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            1 :         case StorageModelType::LiIonNmcBattery: {
    3290            1 :             lifeCalculation_ = (Util::SameString(state.dataIPShortCut->cAlphaArgs(4), "KandlerSmith") || state.dataIPShortCut->lAlphaFieldBlanks(4));
    3291            1 :             seriesNum_ = static_cast<int>(state.dataIPShortCut->rNumericArgs(2));
    3292            1 :             parallelNum_ = static_cast<int>(state.dataIPShortCut->rNumericArgs(3));
    3293            1 :             startingSOC_ = state.dataIPShortCut->lNumericFieldBlanks(4) ? 0.5 : state.dataIPShortCut->rNumericArgs(4);
    3294            1 :             liIon_dcToDcChargingEff_ = state.dataIPShortCut->lNumericFieldBlanks(5) ? 0.95 : state.dataIPShortCut->rNumericArgs(5);
    3295            1 :             liIon_mass_ = state.dataIPShortCut->rNumericArgs(6);
    3296            1 :             liIon_surfaceArea_ = state.dataIPShortCut->rNumericArgs(7);
    3297            1 :             liIon_Cp_ = state.dataIPShortCut->lNumericFieldBlanks(8) ? 1500.0 : state.dataIPShortCut->rNumericArgs(8);
    3298            1 :             liIon_heatTransferCoef_ = state.dataIPShortCut->lNumericFieldBlanks(9) ? 7.5 : state.dataIPShortCut->rNumericArgs(9);
    3299            1 :             liIon_Vfull_ = state.dataIPShortCut->lNumericFieldBlanks(10) ? 4.2 : state.dataIPShortCut->rNumericArgs(10);
    3300            1 :             liIon_Vexp_ = state.dataIPShortCut->lNumericFieldBlanks(11) ? 3.53 : state.dataIPShortCut->rNumericArgs(11);
    3301            1 :             liIon_Vnom_ = state.dataIPShortCut->lNumericFieldBlanks(12) ? 3.342 : state.dataIPShortCut->rNumericArgs(12);
    3302            1 :             if (liIon_Vfull_ < liIon_Vexp_ || liIon_Vexp_ < liIon_Vnom_) {
    3303            0 :                 ShowSevereError(
    3304              :                     state,
    3305            0 :                     format(
    3306            0 :                         "{}{}=\"{}\", invalid entry.", routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
    3307            0 :                 ShowContinueError(state,
    3308            0 :                                   format("{} must be greater than {},",
    3309            0 :                                          state.dataIPShortCut->cNumericFieldNames(10),
    3310            0 :                                          state.dataIPShortCut->cNumericFieldNames(11)));
    3311            0 :                 ShowContinueError(state, format("which must be greater than {}.", state.dataIPShortCut->cNumericFieldNames(12)));
    3312            0 :                 for (int i = 10; i <= 12; ++i) {
    3313            0 :                     ShowContinueError(state,
    3314            0 :                                       format("{} = {:.3R}", state.dataIPShortCut->cNumericFieldNames(i), state.dataIPShortCut->rNumericArgs(i)));
    3315              :                 }
    3316            0 :                 errorsFound = true;
    3317              :             }
    3318            1 :             liIon_Vnom_default_ = state.dataIPShortCut->lNumericFieldBlanks(13) ? 3.342 : state.dataIPShortCut->rNumericArgs(13);
    3319            1 :             liIon_Qfull_ = state.dataIPShortCut->lNumericFieldBlanks(14) ? 3.2 : state.dataIPShortCut->rNumericArgs(14);
    3320            1 :             liIon_Qexp_ =
    3321            1 :                 state.dataIPShortCut->lNumericFieldBlanks(15) ? 0.8075 * liIon_Qfull_ : state.dataIPShortCut->rNumericArgs(15) * liIon_Qfull_;
    3322            1 :             liIon_Qnom_ =
    3323            1 :                 state.dataIPShortCut->lNumericFieldBlanks(16) ? 0.976875 * liIon_Qfull_ : state.dataIPShortCut->rNumericArgs(16) * liIon_Qfull_;
    3324            1 :             if (liIon_Qexp_ >= liIon_Qnom_) {
    3325            0 :                 ShowSevereError(
    3326              :                     state,
    3327            0 :                     format(
    3328            0 :                         "{}{}=\"{}\", invalid entry.", routineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
    3329            0 :                 ShowContinueError(state,
    3330            0 :                                   format("{} must be greater than {}.",
    3331            0 :                                          state.dataIPShortCut->cNumericFieldNames(16),
    3332            0 :                                          state.dataIPShortCut->cNumericFieldNames(15)));
    3333            0 :                 for (int i = 15; i <= 16; ++i) {
    3334            0 :                     ShowContinueError(state,
    3335            0 :                                       format("{} = {:.3R}", state.dataIPShortCut->cNumericFieldNames(i), state.dataIPShortCut->rNumericArgs(i)));
    3336              :                 }
    3337            0 :                 errorsFound = true;
    3338              :             }
    3339            1 :             liIon_C_rate_ = state.dataIPShortCut->lNumericFieldBlanks(17) ? 1.0 : state.dataIPShortCut->rNumericArgs(17);
    3340            1 :             internalR_ = state.dataIPShortCut->lNumericFieldBlanks(18) ? 0.09 : state.dataIPShortCut->rNumericArgs(18);
    3341              : 
    3342            1 :             maxAhCapacity_ = liIon_Qfull_ * parallelNum_;
    3343              : 
    3344            1 :             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            1 :                 if (lifeCalculation_) {
    3351            1 :                     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            2 :                 ssc_battery_ = std::unique_ptr<battery_t>(
    3361            1 :                     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            1 :                                                              startingSOC_ * 100.0,
    3365              :                                                              100.0, // Reset later
    3366              :                                                              0.0,   // Reset later
    3367            1 :                                                              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            1 :                                                         state.dataHVACGlobal->TimeStepSys),
    3380              :                                   battLifetime,
    3381            1 :                                   new thermal_t(state.dataHVACGlobal->TimeStepSys,
    3382              :                                                 liIon_mass_,
    3383              :                                                 liIon_surfaceArea_,
    3384            1 :                                                 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            1 :                                                 ),
    3389            5 :                                   nullptr));
    3390            1 :                 ssc_lastBatteryState_ = std::make_unique<battery_state>(ssc_battery_->get_state());
    3391            1 :                 ssc_lastBatteryTimeStep_ = ssc_battery_->get_params().dt_hr;
    3392            1 :                 ssc_initBatteryState_ = std::make_unique<battery_state>(ssc_battery_->get_state());
    3393              :             }
    3394              : 
    3395            1 :             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            2 :             SetupOutputVariable(state,
    3407              :                                 "Electric Storage Operating Mode Index",
    3408              :                                 Constant::Units::None,
    3409            2 :                                 storageMode_,
    3410              :                                 OutputProcessor::TimeStepType::System,
    3411              :                                 OutputProcessor::StoreType::Average,
    3412            2 :                                 name_);
    3413            4 :             SetupOutputVariable(state,
    3414              :                                 "Electric Storage Battery Charge State",
    3415              :                                 Constant::Units::Ah,
    3416            2 :                                 absoluteSOC_,
    3417              :                                 OutputProcessor::TimeStepType::System,
    3418              :                                 OutputProcessor::StoreType::Average,
    3419            2 :                                 name_); // issue #4921
    3420            4 :             SetupOutputVariable(state,
    3421              :                                 "Electric Storage Charge Fraction",
    3422              :                                 Constant::Units::None,
    3423            2 :                                 fractionSOC_,
    3424              :                                 OutputProcessor::TimeStepType::System,
    3425              :                                 OutputProcessor::StoreType::Average,
    3426            2 :                                 name_);
    3427            4 :             SetupOutputVariable(state,
    3428              :                                 "Electric Storage Total Current",
    3429              :                                 Constant::Units::A,
    3430            2 :                                 batteryCurrent_,
    3431              :                                 OutputProcessor::TimeStepType::System,
    3432              :                                 OutputProcessor::StoreType::Average,
    3433            2 :                                 name_);
    3434            4 :             SetupOutputVariable(state,
    3435              :                                 "Electric Storage Total Voltage",
    3436              :                                 Constant::Units::V,
    3437            2 :                                 batteryVoltage_,
    3438              :                                 OutputProcessor::TimeStepType::System,
    3439              :                                 OutputProcessor::StoreType::Average,
    3440            2 :                                 name_);
    3441              : 
    3442            2 :             if (lifeCalculation_) {
    3443            4 :                 SetupOutputVariable(state,
    3444              :                                     "Electric Storage Degradation Fraction",
    3445              :                                     Constant::Units::None,
    3446            2 :                                     batteryDamage_,
    3447              :                                     OutputProcessor::TimeStepType::System,
    3448              :                                     OutputProcessor::StoreType::Average,
    3449            2 :                                     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            2 :             if (storageModelMode_ == StorageModelType::SimpleBucketStorage) {
    3510            2 :                 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            2 :             SetupOutputVariable(state,
    3517              :                                 "Electric Storage Battery Temperature",
    3518              :                                 Constant::Units::C,
    3519            1 :                                 batteryTemperature_,
    3520              :                                 OutputProcessor::TimeStepType::System,
    3521              :                                 OutputProcessor::StoreType::Average,
    3522            1 :                                 name_);
    3523              :         }
    3524              : 
    3525            9 :         if (zoneNum_ > 0) {
    3526            2 :             switch (storageModelMode_) {
    3527            2 :             case StorageModelType::SimpleBucketStorage: {
    3528            2 :                 SetupZoneInternalGain(
    3529              :                     state, zoneNum_, name_, DataHeatBalance::IntGainType::ElectricLoadCenterStorageSimple, &qdotConvZone_, nullptr, &qdotRadZone_);
    3530            2 :                 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            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    3561              :     }
    3562            9 : }
    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            0 :             ShowSevereError(state,
    3573            0 :                             format("ElectricStorage charge efficiency was too low.  This occurred for electric storage unit named {}", deviceName));
    3574            0 :             ShowContinueError(state, "Please check your input value  for this electric storage unit and fix the charge efficiency.");
    3575            0 :             errorsFound = true;
    3576            0 :             return minChargeEfficiency;
    3577              :         } else {
    3578            7 :             return userInputValue;
    3579              :         }
    3580              :     } else { // discharging
    3581            7 :         if (userInputValue < minDischargeEfficiency) {
    3582            0 :             ShowSevereError(
    3583            0 :                 state, format("ElectricStorage discharge efficiency was too low.  This occurred for electric storage unit named {}", deviceName));
    3584            0 :             ShowContinueError(state, "Please check your input value  for this electric storage unit and fix the discharge efficiency.");
    3585            0 :             errorsFound = true;
    3586            0 :             return minDischargeEfficiency;
    3587              :         } else {
    3588            7 :             return userInputValue;
    3589              :         }
    3590              :     }
    3591              : }
    3592              : 
    3593            1 : void checkChargeDischargeVoltageCurves(
    3594              :     EnergyPlusData &state, std::string_view nameBatt, Real64 const E0c, Real64 const E0d, Curve::Curve *chargeCurve, Curve::Curve *dischargeCurve)
    3595              : {
    3596            1 :     int constexpr numChecks = 50;  // number of divisions for checking the functions from 0 to 1 for fraction charged/discharged
    3597            1 :     int constexpr numReports = 10; // number of divisions for reporting
    3598            1 :     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           52 :     for (int loop = 1; loop <= numChecks + 1; ++loop) {
    3604           51 :         Real64 xfc = float(loop - 1) / float(numChecks);                   // = q0/qmax
    3605           51 :         Real64 xfd = 1.0 - xfc;                                            // = (qmax-q0)/qmax = 1 - xfc
    3606           51 :         Real64 chargeVoltage = E0d + chargeCurve->value(state, xfc);       // E0d+Ac*xfc+Cc*xfc/(Dc-xfc)
    3607           51 :         Real64 dischargeVoltage = E0c + dischargeCurve->value(state, xfd); // E0c+Ad*xfd+Cd*xfd/(Dd-xfd)
    3608           51 :         if (dischargeVoltage > chargeVoltage) {
    3609            0 :             gotErrs = true;
    3610            0 :             break;
    3611              :         }
    3612              :     }
    3613            1 :     if (gotErrs) {
    3614            0 :         ShowWarningMessage(state, format("Kinetic Battery Model: {} has a charging/discharging voltage curve conflict.", nameBatt));
    3615            0 :         ShowContinueError(state,
    3616              :                           "Discharging voltage is higher than charging voltage which may potentially lead to an imbalance in the stored energy.");
    3617            0 :         ShowContinueError(state, "Check the charging and discharging curves to make sure that the charging voltage is greater than discharging.");
    3618            0 :         ShowContinueError(state, "Also check the charging and discharging energy outputs to find any discrepancies.");
    3619            0 :         for (int loop = 1; loop <= numReports + 1; ++loop) {
    3620            0 :             Real64 xfc = float(loop - 1) / float(numReports);                  // = q0/qmax
    3621            0 :             Real64 xfd = 1.0 - xfc;                                            // = (qmax-q0)/qmax = 1 - xfc
    3622            0 :             Real64 chargeVoltage = E0d + chargeCurve->value(state, xfc);       // E0d+Ac*xfc+Cc*xfc/(Dc-xfc)
    3623            0 :             Real64 dischargeVoltage = E0c + dischargeCurve->value(state, xfd); // E0c+Ad*xfd+Cd*xfd/(Dd-xfd)
    3624            0 :             ShowContinueError(
    3625              :                 state,
    3626            0 :                 format(
    3627              :                     "Charged fraction = {:.1R}, Charging voltage = {:.3R} V, Discharging voltage = {:.3R} V", xfc, chargeVoltage, dischargeVoltage));
    3628              :         }
    3629              :     }
    3630            1 : }
    3631              : 
    3632           83 : void ElectricStorage::reinitAtBeginEnvironment()
    3633              : {
    3634           83 :     pelNeedFromStorage_ = 0.0;
    3635           83 :     pelFromStorage_ = 0.0;
    3636           83 :     pelIntoStorage_ = 0.0;
    3637           83 :     qdotConvZone_ = 0.0;
    3638           83 :     qdotRadZone_ = 0.0;
    3639           83 :     timeElapsed_ = 0.0;
    3640           83 :     electEnergyinStorage_ = 0.0;
    3641           83 :     storedPower_ = 0.0;
    3642           83 :     storedEnergy_ = 0.0;
    3643           83 :     decrementedEnergyStored_ = 0.0;
    3644           83 :     drawnPower_ = 0.0;
    3645           83 :     drawnEnergy_ = 0.0;
    3646           83 :     thermLossRate_ = 0.0;
    3647           83 :     thermLossEnergy_ = 0.0;
    3648           83 :     lastTimeStepStateOfCharge_ = startingEnergyStored_;
    3649           83 :     thisTimeStepStateOfCharge_ = startingEnergyStored_;
    3650              : 
    3651           83 :     if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3652            9 :         Real64 initialCharge = maxAhCapacity_ * startingSOC_;
    3653            9 :         lastTwoTimeStepAvailable_ = initialCharge * availableFrac_;
    3654            9 :         lastTwoTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3655            9 :         lastTimeStepAvailable_ = initialCharge * availableFrac_;
    3656            9 :         lastTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3657            9 :         thisTimeStepAvailable_ = initialCharge * availableFrac_;
    3658            9 :         thisTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3659            9 :         if (lifeCalculation_) {
    3660            9 :             count0_ = 1;            // Index 0 is for initial SOC, so new input starts from index 1.
    3661            9 :             b10_[0] = startingSOC_; // the initial fractional SOC is stored as the reference
    3662            9 :             x0_[0] = 0.0;
    3663          909 :             for (int loop = 1; loop < maxRainflowArrayBounds_ + 1; ++loop) {
    3664          900 :                 b10_[loop] = 0.0;
    3665          900 :                 x0_[loop] = 0.0;
    3666              :             }
    3667           54 :             for (int loop = 0; loop < cycleBinNum_; ++loop) {
    3668           45 :                 oneNmb0_[loop] = 0.0;
    3669           45 :                 nmb0_[loop] = 0.0;
    3670              :             }
    3671            9 :             batteryDamage_ = 0.0;
    3672              :         }
    3673           74 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3674              :         // Copy the initial battery state to the last battery state
    3675            9 :         *ssc_lastBatteryState_ = *ssc_initBatteryState_;
    3676            9 :         ssc_lastBatteryTimeStep_ = ssc_initBatteryTimeStep_;
    3677            9 :         ssc_battery_->set_state(*ssc_lastBatteryState_, ssc_initBatteryTimeStep_);
    3678              :     }
    3679           83 :     myWarmUpFlag_ = true;
    3680           83 : }
    3681              : 
    3682           74 : void ElectricStorage::reinitZoneGainsAtBeginEnvironment()
    3683              : {
    3684           74 :     qdotConvZone_ = 0.0;
    3685           74 :     qdotRadZone_ = 0.0;
    3686           74 : }
    3687              : 
    3688           36 : void ElectricStorage::reinitAtEndWarmup()
    3689              : {
    3690              :     // need to reset initial state of charge at beginning of environment but after warm up is complete
    3691           36 :     lastTimeStepStateOfCharge_ = startingEnergyStored_;
    3692           36 :     thisTimeStepStateOfCharge_ = startingEnergyStored_;
    3693           36 :     if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3694            4 :         Real64 initialCharge = maxAhCapacity_ * startingSOC_;
    3695            4 :         lastTwoTimeStepAvailable_ = initialCharge * availableFrac_;
    3696            4 :         lastTwoTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3697            4 :         lastTimeStepAvailable_ = initialCharge * availableFrac_;
    3698            4 :         lastTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3699            4 :         thisTimeStepAvailable_ = initialCharge * availableFrac_;
    3700            4 :         thisTimeStepBound_ = initialCharge * (1.0 - availableFrac_);
    3701            4 :         if (lifeCalculation_) {
    3702            4 :             count0_ = 1;            // Index 0 is for initial SOC, so new input starts from index 1.
    3703            4 :             b10_[0] = startingSOC_; // the initial fractional SOC is stored as the reference
    3704            4 :             x0_[0] = 0.0;
    3705          404 :             for (int loop = 1; loop < maxRainflowArrayBounds_ + 1; ++loop) {
    3706          400 :                 b10_[loop] = 0.0;
    3707          400 :                 x0_[loop] = 0.0;
    3708              :             }
    3709           24 :             for (int loop = 0; loop < cycleBinNum_; ++loop) {
    3710           20 :                 oneNmb0_[loop] = 0.0;
    3711           20 :                 nmb0_[loop] = 0.0;
    3712              :             }
    3713            4 :             batteryDamage_ = 0.0;
    3714              :         }
    3715           32 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3716              :         // Copy the initial battery state to the last battery state
    3717            4 :         *ssc_lastBatteryState_ = *ssc_initBatteryState_;
    3718            4 :         ssc_lastBatteryTimeStep_ = ssc_initBatteryTimeStep_;
    3719            4 :         ssc_battery_->set_state(*ssc_lastBatteryState_, ssc_lastBatteryTimeStep_);
    3720              :     }
    3721           36 :     myWarmUpFlag_ = false;
    3722           36 : }
    3723              : 
    3724       130975 : void ElectricStorage::timeCheckAndUpdate(EnergyPlusData &state)
    3725              : {
    3726              : 
    3727       130975 :     if (myWarmUpFlag_ && !state.dataGlobal->WarmupFlag) {
    3728           36 :         reinitAtEndWarmup();
    3729              :     }
    3730              : 
    3731              :     Real64 timeElapsedLoc =
    3732       130975 :         state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
    3733       130975 :     if (timeElapsed_ != timeElapsedLoc) { // time changed, update last with "current" result from previous time
    3734        52310 :         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         4483 :             Real64 deltaSOC1 = thisTimeStepAvailable_ + thisTimeStepBound_ - lastTimeStepAvailable_ - lastTimeStepBound_;
    3742         4483 :             deltaSOC1 /= maxAhCapacity_;
    3743              : 
    3744              :             //      Calculate the fractional SOC change between the "previous one" time step and the "previous two" time steps
    3745         4483 :             Real64 deltaSOC2 = lastTimeStepAvailable_ + lastTimeStepBound_ - lastTwoTimeStepAvailable_ - lastTwoTimeStepBound_;
    3746         4483 :             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         4483 :             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         1471 :                 Real64 input0 = (lastTimeStepAvailable_ + lastTimeStepBound_) / maxAhCapacity_;
    3755         1471 :                 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         1471 :                 int constexpr maxRainflowArrayInc_ = 100;
    3761              : 
    3762         1471 :                 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         1471 :                 rainflow(cycleBinNum_, input0, b10_, x0_, count0_, nmb0_, oneNmb0_);
    3769              : 
    3770         1471 :                 batteryDamage_ = 0.0;
    3771              : 
    3772         8826 :                 for (int binNum = 0; binNum < cycleBinNum_; ++binNum) {
    3773              :                     //       Battery damage is calculated by accumulating the impact from each cycle.
    3774         7355 :                     batteryDamage_ += oneNmb0_[binNum] / lifeCurve_->value(state, (double(binNum) / double(cycleBinNum_)));
    3775              :                 }
    3776              :             }
    3777        52310 :         } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3778         4483 :             *ssc_lastBatteryState_ = ssc_battery_->get_state();
    3779         4483 :             ssc_lastBatteryTimeStep_ = ssc_battery_->get_params().dt_hr;
    3780              :         }
    3781              : 
    3782        52310 :         lastTimeStepStateOfCharge_ = thisTimeStepStateOfCharge_;
    3783        52310 :         lastTwoTimeStepAvailable_ = lastTimeStepAvailable_;
    3784        52310 :         lastTwoTimeStepBound_ = lastTimeStepBound_;
    3785        52310 :         lastTimeStepAvailable_ = thisTimeStepAvailable_;
    3786        52310 :         lastTimeStepBound_ = thisTimeStepBound_;
    3787        52310 :         timeElapsed_ = timeElapsedLoc;
    3788              : 
    3789              :     } // end if time changed
    3790       130975 : }
    3791              : 
    3792       130975 : 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       130975 :     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       130975 :     if (storageModelMode_ == StorageModelType::SimpleBucketStorage) {
    3809       109605 :         simulateSimpleBucketModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3810        21370 :     } else if (storageModelMode_ == StorageModelType::KIBaMBattery) {
    3811        10685 :         simulateKineticBatteryModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3812        10685 :     } else if (storageModelMode_ == StorageModelType::LiIonNmcBattery) {
    3813        10685 :         simulateLiIonNmcBatteryModel(state, powerCharge, powerDischarge, charging, discharging, controlSOCMaxFracLimit, controlSOCMinFracLimit);
    3814              :     }
    3815       130975 : }
    3816              : 
    3817            9 : std::string const &ElectricStorage::name() const
    3818              : {
    3819            9 :     return name_;
    3820              : }
    3821              : 
    3822       109605 : 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       109605 :     if (charging) {
    3835              : 
    3836        35274 :         if (lastTimeStepStateOfCharge_ >= (maxEnergyCapacity_ * controlSOCMaxFracLimit)) {
    3837              :             // storage full!  no more allowed!
    3838            0 :             powerCharge = 0.0;
    3839            0 :             charging = false;
    3840              :         }
    3841        35274 :         if (powerCharge > maxPowerStore_) {
    3842        14713 :             powerCharge = maxPowerStore_;
    3843              :         }
    3844              : 
    3845              :         // now check to see if charge would exceed capacity, and modify to just fill physical storage cap
    3846        35274 :         if ((lastTimeStepStateOfCharge_ + powerCharge * state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_) >=
    3847        35274 :             (maxEnergyCapacity_ * controlSOCMaxFracLimit)) {
    3848            0 :             powerCharge = ((maxEnergyCapacity_ * controlSOCMaxFracLimit) - lastTimeStepStateOfCharge_) /
    3849            0 :                           (state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_);
    3850              :         }
    3851              :     } // charging
    3852              : 
    3853       109605 :     if (discharging) {
    3854              : 
    3855        64561 :         if (lastTimeStepStateOfCharge_ <= (maxEnergyCapacity_ * controlSOCMinFracLimit)) {
    3856              :             // storage empty  no more allowed!
    3857         2286 :             powerDischarge = 0.0;
    3858         2286 :             discharging = false;
    3859              :         }
    3860        64561 :         if (powerDischarge > maxPowerDraw_) {
    3861        11787 :             powerDischarge = maxPowerDraw_;
    3862              :         }
    3863              :         // now check if will empty this timestep, power draw is amplified by energetic effic
    3864        64561 :         if ((lastTimeStepStateOfCharge_ - powerDischarge * state.dataHVACGlobal->TimeStepSysSec / energeticEfficDischarge_) <=
    3865        64561 :             (maxEnergyCapacity_ * controlSOCMinFracLimit)) {
    3866         4578 :             powerDischarge = (lastTimeStepStateOfCharge_ - (maxEnergyCapacity_ * controlSOCMinFracLimit)) * energeticEfficDischarge_ /
    3867         2289 :                              (state.dataHVACGlobal->TimeStepSysSec);
    3868              :         }
    3869              :     }
    3870              : 
    3871       109605 :     if ((!charging) && (!discharging)) {
    3872        12056 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_;
    3873        12056 :         pelIntoStorage_ = 0.0;
    3874        12056 :         pelFromStorage_ = 0.0;
    3875              :     }
    3876       109605 :     if (charging) {
    3877        35274 :         pelIntoStorage_ = powerCharge;
    3878        35274 :         pelFromStorage_ = 0.0;
    3879        35274 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_ + powerCharge * state.dataHVACGlobal->TimeStepSysSec * energeticEfficCharge_;
    3880              :     }
    3881       109605 :     if (discharging) {
    3882        62275 :         pelIntoStorage_ = 0.0;
    3883        62275 :         pelFromStorage_ = powerDischarge;
    3884        62275 :         thisTimeStepStateOfCharge_ = lastTimeStepStateOfCharge_ - powerDischarge * state.dataHVACGlobal->TimeStepSysSec / energeticEfficDischarge_;
    3885        62275 :         thisTimeStepStateOfCharge_ = max(thisTimeStepStateOfCharge_, 0.0);
    3886              :     }
    3887              : 
    3888              :     // updates and reports
    3889       109605 :     electEnergyinStorage_ = thisTimeStepStateOfCharge_; //[J]
    3890       109605 :     storedPower_ = pelIntoStorage_;
    3891       109605 :     storedEnergy_ = pelIntoStorage_ * state.dataHVACGlobal->TimeStepSysSec;
    3892       109605 :     decrementedEnergyStored_ = -1.0 * storedEnergy_;
    3893       109605 :     drawnPower_ = pelFromStorage_;
    3894       109605 :     drawnEnergy_ = pelFromStorage_ * state.dataHVACGlobal->TimeStepSysSec;
    3895       109605 :     thermLossRate_ = max(storedPower_ * (1.0 - energeticEfficCharge_), drawnPower_ * (1.0 - energeticEfficDischarge_));
    3896       109605 :     thermLossEnergy_ = thermLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    3897              : 
    3898       109605 :     if (zoneNum_ > 0) { // set values for zone heat gains
    3899        29548 :         qdotConvZone_ = (1.0 - zoneRadFract_) * thermLossRate_;
    3900        29548 :         qdotRadZone_ = (zoneRadFract_)*thermLossRate_;
    3901              :     }
    3902       109605 : }
    3903              : 
    3904        10685 : 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        10685 :     Real64 I0 = 0.0;
    3915        10685 :     Real64 Volt = 0.0;
    3916              : 
    3917        10685 :     Real64 T0 = 0.0;
    3918        10685 :     Real64 qmaxf = 0.0;
    3919        10685 :     Real64 Ef = 0.0;
    3920        10685 :     Real64 q0 = 0.0;
    3921              : 
    3922        10685 :     Real64 qmax = maxAhCapacity_;
    3923        10685 :     Real64 E0c = chargedOCV_;
    3924        10685 :     Real64 E0d = dischargedOCV_;
    3925        10685 :     Real64 k = chargeConversionRate_;
    3926        10685 :     Real64 c = availableFrac_;
    3927              : 
    3928        10685 :     if (charging) {
    3929              : 
    3930              :         //*************************************************
    3931              :         // The sign of power and current is negative in charging
    3932              :         //*************************************************
    3933         2134 :         Real64 Pw = -powerCharge / numBattery_;
    3934         2134 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    3935         2134 :         if (q0 > qmax * controlSOCMaxFracLimit) {
    3936              :             // stop charging with controller signal for max state of charge
    3937          558 :             Pw = 0.0;
    3938          558 :             powerCharge = 0.0;
    3939          558 :             charging = false;
    3940          558 :             storageMode_ = 0;
    3941          558 :             storedPower_ = 0.0;
    3942          558 :             storedEnergy_ = 0.0;
    3943          558 :             decrementedEnergyStored_ = 0.0;
    3944          558 :             drawnPower_ = 0.0;
    3945          558 :             drawnEnergy_ = 0.0;
    3946         5676 :             return;
    3947              :         }
    3948              : 
    3949         1576 :         I0 = 1.0;                                                                                       // Initial assumption
    3950         1576 :         T0 = std::abs(qmax / I0);                                                                       // Initial Assumption
    3951         1576 :         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         1576 :         Real64 Xf = q0 / qmaxf;
    3953         1576 :         Ef = E0d + chargeCurve_->value(state, Xf); // E0d+Ac*Xf+Cc*Xf/(Dc-Xf) (use curve)
    3954         1576 :         Volt = Ef - I0 * internalR_;
    3955         1576 :         Real64 Inew = 0.0;
    3956         1576 :         if (Volt != 0.0) {
    3957         1576 :             Inew = Pw / Volt;
    3958              :         }
    3959         1576 :         Real64 Tnew = 0.0;
    3960         1576 :         if (Inew != 0.0) {
    3961         1576 :             Tnew = qmaxf / std::abs(Inew);
    3962              :         }
    3963         1576 :         Real64 error = 1.0;
    3964              : 
    3965         8717 :         while (error > 0.0001) { // Iteration process to get converged current(I)
    3966         7141 :             I0 = Inew;
    3967         7141 :             T0 = Tnew;
    3968         7141 :             qmaxf = qmax * k * c * T0 / (1.0 - std::exp(-k * T0) + c * (k * T0 - 1.0 + std::exp(-k * T0)));
    3969         7141 :             Xf = q0 / qmaxf;
    3970         7141 :             Ef = E0d + chargeCurve_->value(state, Xf); // E0d+Ac*Xf+Cc*Xf/(Dc-Xf) (use curve)
    3971         7141 :             Volt = Ef - I0 * internalR_;
    3972         7141 :             Inew = Pw / Volt;
    3973         7141 :             Tnew = std::abs(qmaxf / Inew); // ***Always positive here
    3974         7141 :             error = std::abs(Inew - I0);
    3975              :         }
    3976              : 
    3977         1576 :         Real64 dividend = -k * c * qmax + k * lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    3978         1576 :                           q0 * k * c * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    3979         1576 :         Real64 divisor = 1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    3980         1576 :                          c * (k * state.dataHVACGlobal->TimeStepSys - 1 + std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    3981         1576 :         Real64 Imax = dividend / divisor;
    3982              :         // Below: This is the limit of charging current from Charge Rate Limit (input)
    3983         1576 :         Imax = max(Imax, -(qmax - q0) * maxChargeRate_);
    3984              : 
    3985         1576 :         if (std::abs(I0) <= std::abs(Imax)) {
    3986          653 :             I0 = Pw / Volt;
    3987              :             // Pactual = I0 * Volt;
    3988              :         } else {
    3989          923 :             I0 = Imax;
    3990          923 :             qmaxf = 80.0; // Initial assumption to solve the equation using iterative method
    3991          923 :             error = 10.0; // Initial assumption ...
    3992         5724 :             while (error > 0.001) {
    3993              :                 // *** I0(current) should be positive for this calculation
    3994         4801 :                 Real64 RHS = (qmax * k * c * qmaxf / std::abs(I0)) /
    3995         4801 :                              (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         4801 :                 error = std::abs(qmaxf - RHS);
    3997         4801 :                 qmaxf = RHS;
    3998              :             }
    3999              :         }
    4000              :     }
    4001              : 
    4002        10127 :     if (discharging) {
    4003              :         //**********************************************
    4004              :         // The sign of power and current is positive in discharging
    4005              :         //**********************************************
    4006              : 
    4007         7068 :         Real64 Pw = powerDischarge / numBattery_;
    4008         7068 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    4009              : 
    4010         7068 :         if (q0 < qmax * controlSOCMinFracLimit) {
    4011              :             // stop discharging with controller signal for min state of charge
    4012         5118 :             Pw = 0.0;
    4013         5118 :             discharging = false;
    4014         5118 :             powerDischarge = 0.0;
    4015         5118 :             storageMode_ = 0;
    4016         5118 :             storedPower_ = 0.0;
    4017         5118 :             storedEnergy_ = 0.0;
    4018         5118 :             decrementedEnergyStored_ = 0.0;
    4019         5118 :             drawnPower_ = 0.0;
    4020         5118 :             drawnEnergy_ = 0.0;
    4021         5118 :             return;
    4022              :         }
    4023              : 
    4024         1950 :         bool const ok = determineCurrentForBatteryDischarge(state, I0, T0, Volt, Pw, q0, dischargeCurve_, k, c, qmax, E0c, internalR_);
    4025         1950 :         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         1950 :         Real64 dividend = k * lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4034         1950 :                           q0 * k * c * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    4035         1950 :         Real64 divisor = 1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4036         1950 :                          c * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys));
    4037         1950 :         Real64 Imax = dividend / divisor;
    4038         1950 :         Imax = min(Imax, maxDischargeI_);
    4039         1950 :         if (std::abs(I0) <= Imax) {
    4040         1757 :             I0 = Pw / Volt;
    4041              :             // Pactual = I0 * Volt;
    4042              :         } else {
    4043          193 :             I0 = Imax;
    4044          193 :             qmaxf = 10.0;        // Initial assumption to solve the equation using iterative method
    4045          193 :             Real64 error = 10.0; // Initial assumption ...
    4046         1862 :             while (error > 0.001) {
    4047         1669 :                 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         1669 :                 error = std::abs(qmaxf - RHS);
    4049         1669 :                 qmaxf = RHS;
    4050              :             }
    4051          193 :             Real64 Xf = (qmax - q0) / qmaxf;
    4052          193 :             Ef = E0c + dischargeCurve_->value(state, Xf);
    4053          193 :             Volt = Ef - I0 * internalR_;
    4054              :         }
    4055         1950 :         if (Volt < cutoffV_) {
    4056            2 :             I0 = 0.0;
    4057              :         }
    4058              :     } // if discharging
    4059              : 
    4060         5009 :     if ((!charging) && (!discharging)) {
    4061         1483 :         thisTimeStepAvailable_ = lastTimeStepAvailable_;
    4062         1483 :         thisTimeStepBound_ = lastTimeStepBound_;
    4063         1483 :         I0 = 0.0;
    4064         1483 :         Volt = 0.0;
    4065         1483 :         q0 = lastTimeStepAvailable_ + lastTimeStepBound_;
    4066              :     } else {
    4067         3526 :         Real64 newAvailable = lastTimeStepAvailable_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4068         3526 :                               (q0 * k * c - I0) * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k -
    4069         3526 :                               I0 * c * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k;
    4070         3526 :         Real64 newBound = lastTimeStepBound_ * std::exp(-k * state.dataHVACGlobal->TimeStepSys) +
    4071         3526 :                           q0 * (1.0 - c) * (1.0 - std::exp(-k * state.dataHVACGlobal->TimeStepSys)) -
    4072         3526 :                           I0 * (1.0 - c) * (k * state.dataHVACGlobal->TimeStepSys - 1.0 + std::exp(-k * state.dataHVACGlobal->TimeStepSys)) / k;
    4073         3526 :         thisTimeStepAvailable_ = max(0.0, newAvailable);
    4074         3526 :         thisTimeStepBound_ = max(0.0, newBound);
    4075              :     }
    4076              : 
    4077              :     // Pactual = I0 * Volt;
    4078         5009 :     Real64 TotalSOC = thisTimeStepAvailable_ + thisTimeStepBound_;
    4079              : 
    4080              :     // output1
    4081         5009 :     if (TotalSOC > q0) {
    4082         1578 :         storageMode_ = 2;
    4083         1578 :         storedPower_ = -1.0 * Volt * I0 * numBattery_; // Issue #5303, fix sign issue
    4084         1578 :         storedEnergy_ = -1.0 * Volt * I0 * numBattery_ * state.dataHVACGlobal->TimeStepSysSec;
    4085         1578 :         decrementedEnergyStored_ = -1.0 * storedEnergy_;
    4086         1578 :         drawnPower_ = 0.0;
    4087         1578 :         drawnEnergy_ = 0.0;
    4088              : 
    4089         3431 :     } else if (TotalSOC < q0) {
    4090         1948 :         storageMode_ = 1;
    4091         1948 :         storedPower_ = 0.0;
    4092         1948 :         storedEnergy_ = 0.0;
    4093         1948 :         decrementedEnergyStored_ = 0.0;
    4094         1948 :         drawnPower_ = Volt * I0 * numBattery_;
    4095         1948 :         drawnEnergy_ = Volt * I0 * numBattery_ * state.dataHVACGlobal->TimeStepSysSec;
    4096              : 
    4097              :     } else {
    4098         1483 :         storageMode_ = 0;
    4099         1483 :         storedPower_ = 0.0;
    4100         1483 :         storedEnergy_ = 0.0;
    4101         1483 :         decrementedEnergyStored_ = 0.0;
    4102         1483 :         drawnPower_ = 0.0;
    4103         1483 :         drawnEnergy_ = 0.0;
    4104              :     }
    4105              : 
    4106         5009 :     absoluteSOC_ = TotalSOC * numBattery_;
    4107         5009 :     fractionSOC_ = TotalSOC / qmax;
    4108         5009 :     batteryCurrent_ = I0 * parallelNum_;
    4109         5009 :     batteryVoltage_ = Volt * seriesNum_;
    4110         5009 :     thermLossRate_ = internalR_ * pow_2(I0) * numBattery_;
    4111         5009 :     thermLossEnergy_ = internalR_ * pow_2(I0) * state.dataHVACGlobal->TimeStepSysSec * numBattery_;
    4112              : 
    4113         5009 :     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         5009 :     powerCharge = storedPower_;
    4119         5009 :     powerDischarge = drawnPower_;
    4120              : }
    4121              : 
    4122        10685 : 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        10685 :     int old_excepts = fegetexcept();
    4134        10685 :     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        10685 :     battery_state battState = *ssc_lastBatteryState_;
    4149        10685 :     ssc_battery_->set_state(battState, ssc_lastBatteryTimeStep_);
    4150        10685 :     if (std::lround(ssc_battery_->get_params().dt_hr * 60.0) != std::lround(state.dataHVACGlobal->TimeStepSys * 60.0)) {
    4151         1673 :         ssc_battery_->ChangeTimestep(state.dataHVACGlobal->TimeStepSys);
    4152              :     }
    4153              : 
    4154              :     // Set the temperature the battery sees
    4155        10685 :     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        10685 :         battState.thermal->T_room = state.dataEnvrn->OutDryBulbTemp;
    4161              :     }
    4162              : 
    4163              :     // Set the SOC limits
    4164        10685 :     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        10685 :     double power{0.0}; // Using double instead of Real64 because SSC is expecting a double
    4172        10685 :     if (charging) {
    4173         2134 :         power = -powerCharge;
    4174         8551 :     } else if (discharging) {
    4175         7068 :         power = powerDischarge;
    4176              :     }
    4177        10685 :     power *= 0.001; // Convert to kW
    4178        10685 :     ssc_battery_->runPower(power);
    4179              : 
    4180              :     // Store outputs
    4181        10685 :     const battery_state &battState2{ssc_battery_->get_state()};
    4182        10685 :     if (battState2.P < 0.0) { // negative for charging
    4183          539 :         storageMode_ = 2;
    4184          539 :         powerCharge = fabs(battState2.P) * 1000.0; // kW -> W
    4185          539 :         powerDischarge = 0.0;
    4186          539 :         charging = true;
    4187          539 :         discharging = false;
    4188        10146 :     } else if (battState2.P > 0.0) { // positive for discharging
    4189         1261 :         storageMode_ = 1;
    4190         1261 :         powerCharge = 0.0;
    4191         1261 :         powerDischarge = fabs(battState2.P) * 1000.0; // kW -> W
    4192         1261 :         charging = false;
    4193         1261 :         discharging = true;
    4194              :     } else {
    4195         8885 :         storageMode_ = 0;
    4196         8885 :         powerCharge = 0.0;
    4197         8885 :         powerDischarge = 0.0;
    4198         8885 :         charging = false;
    4199         8885 :         discharging = false;
    4200              :     }
    4201        10685 :     absoluteSOC_ = ssc_battery_->charge_total();
    4202        10685 :     fractionSOC_ = ssc_battery_->SOC() * 0.01; // % -> fraction
    4203        10685 :     batteryCurrent_ = ssc_battery_->I();
    4204        10685 :     batteryVoltage_ = ssc_battery_->V();
    4205        10685 :     batteryDamage_ = 1.0 - (ssc_battery_->charge_maximum_lifetime() / maxAhCapacity_);
    4206        10685 :     storedPower_ = powerCharge;
    4207        10685 :     storedEnergy_ = storedPower_ * state.dataHVACGlobal->TimeStepSysSec;
    4208        10685 :     drawnPower_ = powerDischarge;
    4209        10685 :     drawnEnergy_ = drawnPower_ * state.dataHVACGlobal->TimeStepSysSec;
    4210        10685 :     decrementedEnergyStored_ = -storedEnergy_;
    4211        10685 :     thermLossRate_ = battState2.thermal->heat_dissipated * 1000.0; // kW -> W
    4212        10685 :     thermLossEnergy_ = thermLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4213        10685 :     batteryTemperature_ = battState2.thermal->T_batt;
    4214              : 
    4215              :     // Zone Heat Gains
    4216        10685 :     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        10685 :     feenableexcept(old_excepts);
    4223              : #endif
    4224              : 
    4225              : #ifdef DEBUG_ARITHM_MSVC
    4226              :     _controlfp_s(nullptr, ori_fpcntrl, _MCW_EM);
    4227              : #endif
    4228        10685 : }
    4229              : 
    4230            0 : Real64 ElectricStorage::drawnPower() const
    4231              : {
    4232            0 :     return drawnPower_;
    4233              : }
    4234              : 
    4235            0 : Real64 ElectricStorage::storedPower() const
    4236              : {
    4237            0 :     return storedPower_;
    4238              : }
    4239              : 
    4240            0 : Real64 ElectricStorage::drawnEnergy() const
    4241              : {
    4242            0 :     return drawnEnergy_;
    4243              : }
    4244              : 
    4245            0 : Real64 ElectricStorage::storedEnergy() const
    4246              : {
    4247            0 :     return storedEnergy_;
    4248              : }
    4249              : 
    4250            0 : Real64 ElectricStorage::stateOfChargeFraction() const
    4251              : {
    4252            0 :     return fractionSOC_;
    4253              : }
    4254              : 
    4255            0 : Real64 ElectricStorage::batteryTemperature() const
    4256              : {
    4257            0 :     assert(storageModelMode_ == StorageModelType::LiIonNmcBattery);
    4258            0 :     return batteryTemperature_;
    4259              : }
    4260              : 
    4261         1950 : 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         1950 :     curI0 = 10.0;         // Initial assumption
    4275         1950 :     curT0 = qmax / curI0; // Initial Assumption
    4276         1950 :     Real64 qmaxf = qmax * k * c * curT0 /
    4277         1950 :                    (1.0 - std::exp(-k * curT0) + c * (k * curT0 - 1.0 + std::exp(-k * curT0))); // Initial calculation of a function qmax(I)
    4278         1950 :     Real64 Xf = (qmax - q0) / qmaxf;
    4279         1950 :     Real64 Ef = E0c + curve->value(state, Xf); // E0d+Ac*Xf+Cc*X/(Dc-Xf)
    4280         1950 :     curVolt = Ef - curI0 * InternalR;
    4281         1950 :     Real64 Inew = Pw / curVolt;
    4282         1950 :     Real64 Tnew = qmaxf / Inew;
    4283         1950 :     Real64 error = 1.0;
    4284         1950 :     int countForIteration = 0;
    4285         1950 :     bool exceedIterationLimit = false;
    4286              : 
    4287        11130 :     while (error > 0.0001) { // Iteration process to get converged current(I)
    4288         9180 :         curI0 = Inew;
    4289         9180 :         curT0 = Tnew;
    4290         9180 :         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         9180 :         if (qmaxf != 0.0) {
    4293         9180 :             Xf = (qmax - q0) / qmaxf;
    4294              :         } else {
    4295            0 :             Xf = 1.0;
    4296              :         }
    4297              : 
    4298         9180 :         Ef = E0c + curve->value(state, Xf); // E0c+Ad*Xf+Cd*X/(Dd-Xf)
    4299         9180 :         curVolt = Ef - curI0 * InternalR;
    4300              :         // add div by zero protection #5301
    4301         9180 :         if (curVolt != 0.0) {
    4302         9180 :             Inew = Pw / curVolt;
    4303              :         } else {
    4304            0 :             Inew = 1.0;
    4305              :         }
    4306              : 
    4307              :         // add div by zero protection #5301
    4308         9180 :         if (Inew != 0.0) {
    4309         9180 :             Tnew = qmaxf / Inew;
    4310              :         } else {
    4311            0 :             Tnew = 1.0;
    4312              :         }
    4313              : 
    4314         9180 :         error = std::abs(Inew - curI0);
    4315         9180 :         ++countForIteration;
    4316         9180 :         if (countForIteration > 1000) {
    4317            0 :             exceedIterationLimit = true;
    4318              :             // Issue #5301 need more diagnostics for this case
    4319            0 :             ShowWarningError(
    4320              :                 state, "ElectricStorage::determineCurrentForBatteryDischarge, iteration limit exceeded, failed to solve for discharge current.");
    4321            0 :             ShowContinueError(state, format("Last timestep charge available, q0 = {:.5R}", q0));
    4322            0 :             ShowContinueError(state, format("New Current, Inew = {:.5R} [Amps]", Inew));
    4323            0 :             ShowContinueError(state, format("Power discharge per module cell, Pw = {:.5R} ", Pw));
    4324            0 :             ShowContinueError(
    4325            0 :                 state, format("Charge Conversion Rate, [1/h] change rate from bound charge energy to available charge, parameter k = {:.5R}", k));
    4326            0 :             ShowContinueError(state, format("parameter c = {:.5R}", c));
    4327            0 :             ShowContinueError(state, format("parameter qmax = {:.5R}", qmax));
    4328            0 :             ShowContinueError(state, format("Fully charged open circuit voltage, parameter E0c  = {:.5R}", E0c));
    4329            0 :             ShowContinueError(state, format("parameter InternalR = {:.5R}", InternalR));
    4330            0 :             if (qmaxf == 0.0) {
    4331            0 :                 ShowContinueError(state, "qmaxf was zero, would have divided by zero.");
    4332              :             }
    4333            0 :             if (Inew == 0.0) {
    4334            0 :                 ShowContinueError(state, "Inew was zero, would have divided by zero. ");
    4335              :             }
    4336            0 :             if (curVolt == 0.0) {
    4337            0 :                 ShowContinueError(state, "curVolt was zero, would have divided by zero. ");
    4338              :             }
    4339              : 
    4340            0 :             ShowContinueErrorTimeStamp(state, "ElectricStorage::determineCurrentForBatteryDischarge ");
    4341            0 :             break;
    4342              :         }
    4343              :     }
    4344         1950 :     return (!exceedIterationLimit);
    4345              : }
    4346              : 
    4347         1471 : 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         1471 :     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         1471 :     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         1461 :         if (X[count] * X[count - 1] >= 0) {
    4388         1433 :             X[count - 1] = B1[count] - B1[count - 2];
    4389         1433 :             shift(B1, count - 1, count, B1); // Get rid of (count-1) row in B1
    4390         1433 :             shift(X, count, count, X);
    4391         1433 :             --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         1461 :         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            4 :             num = nint((std::abs(X[2]) * numbin * 10 + 5) / 10); // Count half cycle
    4398            4 :             Nmb[num] += 0.5;
    4399              :             // B1 = eoshift( B1, 1 ); // Once counting a half cycle, get rid of the value.
    4400            4 :             B1.erase(B1.begin());
    4401            4 :             B1.push_back(0.0);
    4402              :             // X = eoshift( X, 1 );
    4403            4 :             X.erase(X.begin());
    4404            4 :             X.push_back(0.0);
    4405            4 :             --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         1471 :     if (count >= 4) { // count 1 cycle
    4411           56 :         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           10 :             num = nint((std::abs(X[count - 1]) * numbin * 10 + 5) / 10);
    4415           10 :             ++Nmb[num];
    4416              : 
    4417              :             //     X(count-2) = ABS(X(count))-ABS(X(count-1))+ABS(X(count-2))
    4418           10 :             X[count - 2] = B1[count] - B1[count - 3]; // Updating X needs to be done before shift operation below
    4419              : 
    4420           10 :             shift(B1, count - 1, count, B1); // Get rid of two data points one by one
    4421           10 :             shift(B1, count - 2, count, B1); // Delete one point
    4422              : 
    4423           10 :             shift(X, count, count, X);     // Get rid of two data points one by one
    4424           10 :             shift(X, count - 1, count, X); // Delete one point
    4425              : 
    4426           10 :             count -= 2; // If one cycle is counted, two data points are deleted.
    4427           10 :             if (count < 4) {
    4428            6 :                 break; // When only three data points exists, one cycle cannot be counted.
    4429              :             }
    4430              :         }
    4431              :     }
    4432              : 
    4433         1471 :     ++count;
    4434              : 
    4435              :     // Check the rest of the half cycles every time step
    4436         1471 :     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         1471 : }
    4439              : 
    4440         2906 : 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         7461 :     for (ShiftNum = 1; ShiftNum <= m - 1; ++ShiftNum) {
    4454         4555 :         B[ShiftNum] = A[ShiftNum];
    4455              :     }
    4456              : 
    4457         7285 :     for (ShiftNum = m; ShiftNum <= n; ++ShiftNum) {
    4458         4379 :         B[ShiftNum] = A[ShiftNum + 1];
    4459              :     }
    4460         2906 : }
    4461              : 
    4462              : // constructor
    4463           13 : ElectricTransformer::ElectricTransformer(EnergyPlusData &state, std::string const &objectName)
    4464           13 :     : myOneTimeFlag_(true), usageMode_(TransformerUse::Invalid), heatLossesDestination_(ThermalLossDestination::Invalid), zoneNum_(0),
    4465           13 :       zoneRadFrac_(0.0), ratedCapacity_(0.0), factorTempCoeff_(0.0), tempRise_(0.0), eddyFrac_(0.0),
    4466           13 :       performanceInputMode_(TransformerPerformanceInput::Invalid), ratedEfficiency_(0.0), ratedPUL_(0.0), ratedTemp_(0.0), maxPUL_(0.0),
    4467           26 :       considerLosses_(true), ratedNL_(0.0), ratedLL_(0.0), overloadErrorIndex_(0), efficiency_(0.0), powerIn_(0.0), energyIn_(0.0), powerOut_(0.0),
    4468           13 :       energyOut_(0.0), noLoadLossRate_(0.0), noLoadLossEnergy_(0.0), loadLossRate_(0.0), loadLossEnergy_(0.0), thermalLossRate_(0.0),
    4469           13 :       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           13 :     auto &s_ipsc = state.dataIPShortCut;
    4473              : 
    4474           13 :     bool errorsFound = false;
    4475           13 :     int transformerIDFObjectNum = 0;
    4476           13 :     s_ipsc->cCurrentModuleObject = "ElectricLoadCenter:Transformer";
    4477              : 
    4478           13 :     transformerIDFObjectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ElectricLoadCenter:Transformer", objectName);
    4479           13 :     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           26 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    4484           13 :                                                                  s_ipsc->cCurrentModuleObject,
    4485              :                                                                  transformerIDFObjectNum,
    4486           13 :                                                                  s_ipsc->cAlphaArgs,
    4487              :                                                                  numAlphas,
    4488           13 :                                                                  s_ipsc->rNumericArgs,
    4489              :                                                                  numNums,
    4490              :                                                                  IOStat,
    4491           13 :                                                                  s_ipsc->lNumericFieldBlanks,
    4492           13 :                                                                  s_ipsc->lAlphaFieldBlanks,
    4493           13 :                                                                  s_ipsc->cAlphaFieldNames,
    4494           13 :                                                                  s_ipsc->cNumericFieldNames);
    4495              : 
    4496           13 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    4497           13 :         name_ = s_ipsc->cAlphaArgs(1);
    4498              :         // how to verify names are unique across objects? add to GlobalNames?
    4499           13 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    4500            0 :             availSched_ = Sched::GetScheduleAlwaysOn(state);
    4501           13 :         } 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           13 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    4507            0 :             usageMode_ = TransformerUse::PowerInFromGrid; // default
    4508           13 :         } 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           13 :         if (s_ipsc->lAlphaFieldBlanks(4)) {
    4515           13 :             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           13 :         zoneRadFrac_ = s_ipsc->rNumericArgs(1);
    4526           13 :         ratedCapacity_ = s_ipsc->rNumericArgs(2);
    4527              :         // unused phase_ = s_ipsc->rNumericArgs(3);
    4528              : 
    4529           13 :         if (Util::SameString(s_ipsc->cAlphaArgs(5), "Copper")) {
    4530            0 :             factorTempCoeff_ = 234.5;
    4531           13 :         } else if (Util::SameString(s_ipsc->cAlphaArgs(5), "Aluminum")) {
    4532           13 :             factorTempCoeff_ = 225.0;
    4533              :         } else {
    4534            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
    4535            0 :             errorsFound = true;
    4536              :         }
    4537           13 :         tempRise_ = s_ipsc->rNumericArgs(4);
    4538           13 :         eddyFrac_ = s_ipsc->rNumericArgs(5);
    4539              : 
    4540           13 :         performanceInputMode_ = static_cast<TransformerPerformanceInput>(getEnumValue(transformerPerformanceInputNamesUC, s_ipsc->cAlphaArgs(6)));
    4541           13 :         if (performanceInputMode_ == TransformerPerformanceInput::Invalid) {
    4542            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
    4543            0 :             errorsFound = true;
    4544              :         }
    4545              : 
    4546           13 :         if (ratedCapacity_ == 0) {
    4547            0 :             if (performanceInputMode_ == TransformerPerformanceInput::LossesMethod) {
    4548            0 :                 ShowWarningError(state, format("{}{}=\"{}\".", routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4549            0 :                 ShowContinueError(state, format("Specified {} = {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6)));
    4550            0 :                 ShowContinueError(state, format("Specified {} = {:.1R}", s_ipsc->cNumericFieldNames(2), ratedCapacity_));
    4551            0 :                 ShowContinueError(state, "Transformer load and no load losses cannot be calculated with 0.0 rated capacity.");
    4552            0 :                 ShowContinueError(state, "Simulation continues but transformer losses will be set to zero.");
    4553              :             }
    4554              :         }
    4555           13 :         ratedNL_ = s_ipsc->rNumericArgs(6);
    4556           13 :         ratedLL_ = s_ipsc->rNumericArgs(7);
    4557           13 :         ratedEfficiency_ = s_ipsc->rNumericArgs(8);
    4558           13 :         ratedPUL_ = s_ipsc->rNumericArgs(9);
    4559           13 :         ratedTemp_ = s_ipsc->rNumericArgs(10);
    4560           13 :         maxPUL_ = s_ipsc->rNumericArgs(11);
    4561              :         // Check the input for MaxPUL if the performance input method is EfficiencyMethod
    4562           13 :         if (performanceInputMode_ == TransformerPerformanceInput::EfficiencyMethod) {
    4563           12 :             if (s_ipsc->lNumericFieldBlanks(11)) {
    4564           12 :                 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           13 :         BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(7));
    4574           13 :         if (bs != BooleanSwitch::Invalid) {
    4575           13 :             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           13 :         int numAlphaBeforeMeter = 7;
    4582           13 :         int numWiredMeters = numAlphas - numAlphaBeforeMeter;
    4583              : 
    4584           13 :         if (usageMode_ == TransformerUse::PowerInFromGrid) {
    4585              : 
    4586              :             // Provide warning if no meter is wired to a transformer used to get power from the grid
    4587           12 :             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           24 :             wiredMeterNames_.resize(numWiredMeters, "");
    4593           12 :             wiredMeterPtrs_.resize(numWiredMeters, 0);
    4594           12 :             specialMeter_.resize(numWiredMeters, false);
    4595              : 
    4596              :             // Meter check deferred because they may have not been "loaded" yet,
    4597           35 :             for (int loopCount = 0; loopCount < numWiredMeters; ++loopCount) {
    4598           23 :                 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           46 :                 if (Util::SameString(wiredMeterNames_[loopCount], "Electricity:Facility") ||
    4601           46 :                     Util::SameString(wiredMeterNames_[loopCount], "Electricity:HVAC")) {
    4602            0 :                     specialMeter_[loopCount] = true;
    4603              :                 } else {
    4604           23 :                     specialMeter_[loopCount] = false;
    4605              :                 }
    4606              :             }
    4607              :         }
    4608           26 :         SetupOutputVariable(state,
    4609              :                             "Transformer Efficiency",
    4610              :                             Constant::Units::None,
    4611           13 :                             efficiency_,
    4612              :                             OutputProcessor::TimeStepType::System,
    4613              :                             OutputProcessor::StoreType::Average,
    4614           13 :                             name_);
    4615           26 :         SetupOutputVariable(state,
    4616              :                             "Transformer Input Electricity Rate",
    4617              :                             Constant::Units::W,
    4618           13 :                             powerIn_,
    4619              :                             OutputProcessor::TimeStepType::System,
    4620              :                             OutputProcessor::StoreType::Average,
    4621           13 :                             name_);
    4622           26 :         SetupOutputVariable(state,
    4623              :                             "Transformer Input Electricity Energy",
    4624              :                             Constant::Units::J,
    4625           13 :                             energyIn_,
    4626              :                             OutputProcessor::TimeStepType::System,
    4627              :                             OutputProcessor::StoreType::Sum,
    4628           13 :                             name_);
    4629           26 :         SetupOutputVariable(state,
    4630              :                             "Transformer Output Electricity Rate",
    4631              :                             Constant::Units::W,
    4632           13 :                             powerOut_,
    4633              :                             OutputProcessor::TimeStepType::System,
    4634              :                             OutputProcessor::StoreType::Average,
    4635           13 :                             name_);
    4636           26 :         SetupOutputVariable(state,
    4637              :                             "Transformer Output Electricity Energy",
    4638              :                             Constant::Units::J,
    4639           13 :                             energyOut_,
    4640              :                             OutputProcessor::TimeStepType::System,
    4641              :                             OutputProcessor::StoreType::Sum,
    4642           13 :                             name_);
    4643           26 :         SetupOutputVariable(state,
    4644              :                             "Transformer No Load Loss Rate",
    4645              :                             Constant::Units::W,
    4646           13 :                             noLoadLossRate_,
    4647              :                             OutputProcessor::TimeStepType::System,
    4648              :                             OutputProcessor::StoreType::Average,
    4649           13 :                             name_);
    4650           26 :         SetupOutputVariable(state,
    4651              :                             "Transformer No Load Loss Energy",
    4652              :                             Constant::Units::J,
    4653           13 :                             noLoadLossEnergy_,
    4654              :                             OutputProcessor::TimeStepType::System,
    4655              :                             OutputProcessor::StoreType::Sum,
    4656           13 :                             name_);
    4657           26 :         SetupOutputVariable(state,
    4658              :                             "Transformer Load Loss Rate",
    4659              :                             Constant::Units::W,
    4660           13 :                             loadLossRate_,
    4661              :                             OutputProcessor::TimeStepType::System,
    4662              :                             OutputProcessor::StoreType::Average,
    4663           13 :                             name_);
    4664           26 :         SetupOutputVariable(state,
    4665              :                             "Transformer Load Loss Energy",
    4666              :                             Constant::Units::J,
    4667           13 :                             loadLossEnergy_,
    4668              :                             OutputProcessor::TimeStepType::System,
    4669              :                             OutputProcessor::StoreType::Sum,
    4670           13 :                             name_);
    4671           26 :         SetupOutputVariable(state,
    4672              :                             "Transformer Thermal Loss Rate",
    4673              :                             Constant::Units::W,
    4674           13 :                             thermalLossRate_,
    4675              :                             OutputProcessor::TimeStepType::System,
    4676              :                             OutputProcessor::StoreType::Average,
    4677           13 :                             name_);
    4678           26 :         SetupOutputVariable(state,
    4679              :                             "Transformer Thermal Loss Energy",
    4680              :                             Constant::Units::J,
    4681           13 :                             thermalLossEnergy_,
    4682              :                             OutputProcessor::TimeStepType::System,
    4683              :                             OutputProcessor::StoreType::Sum,
    4684           13 :                             name_);
    4685           13 :         if (usageMode_ == TransformerUse::PowerInFromGrid) { // power losses metered as an end use exterior equipment
    4686           24 :             SetupOutputVariable(state,
    4687              :                                 "Transformer Distribution Electricity Loss Energy",
    4688              :                                 Constant::Units::J,
    4689           12 :                                 elecUseMeteredUtilityLosses_,
    4690              :                                 OutputProcessor::TimeStepType::System,
    4691              :                                 OutputProcessor::StoreType::Sum,
    4692           12 :                                 name_,
    4693              :                                 Constant::eResource::Electricity,
    4694              :                                 OutputProcessor::Group::HVAC, // Is this correct?
    4695              :                                 OutputProcessor::EndUseCat::ExteriorEquipment,
    4696              :                                 "Transformer");
    4697              :         }
    4698           13 :         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           13 :         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           13 :         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           13 :     if (errorsFound) {
    4734            0 :         ShowFatalError(state, format("{}Preceding errors terminate program.", routineName));
    4735              :     }
    4736           13 : }
    4737              : 
    4738            0 : Real64 ElectricTransformer::getLossRateForOutputPower(EnergyPlusData &state, Real64 const powerOutOfTransformer)
    4739              : {
    4740            0 :     manageTransformers(state, powerOutOfTransformer);
    4741            0 :     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       146628 : void ElectricTransformer::manageTransformers(EnergyPlusData &state, Real64 const surplusPowerOutFromLoadCenters)
    4751              : {
    4752       146628 :     Real64 constexpr ambTempRef = 20.0; // reference ambient temperature (C)
    4753       146628 :     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           13 :         if (performanceInputMode_ == TransformerPerformanceInput::EfficiencyMethod) {
    4758              : 
    4759           12 :             Real64 resRef = factorTempCoeff_ + tempRise_ + ambTempRef;
    4760           12 :             Real64 resSpecified = factorTempCoeff_ + ratedTemp_;
    4761           12 :             Real64 resRatio = resSpecified / resRef;
    4762           12 :             Real64 factorTempCorr = (1.0 - eddyFrac_) * resRatio + eddyFrac_ * (1.0 / resRatio);
    4763           12 :             Real64 numerator = ratedCapacity_ * ratedPUL_ * (1.0 - ratedEfficiency_);
    4764           12 :             Real64 denominator = ratedEfficiency_ * (1.0 + pow_2(ratedPUL_ / maxPUL_));
    4765              : 
    4766           12 :             ratedNL_ = numerator / denominator;
    4767           12 :             ratedLL_ = ratedNL_ / (factorTempCorr * pow_2(maxPUL_));
    4768              :         }
    4769           13 :         myOneTimeFlag_ = false;
    4770              :     }
    4771              : 
    4772       146628 :     Real64 elecLoad = 0.0;     // transformer load which may be power in or out depending on the usage mode
    4773       146628 :     Real64 pastElecLoad = 0.0; // transformer load at the previous timestep
    4774       146628 :     switch (usageMode_) {
    4775       138120 :     case TransformerUse::PowerInFromGrid: {
    4776       404385 :         for (std::size_t meterNum = 0; meterNum < wiredMeterPtrs_.size(); ++meterNum) {
    4777              : 
    4778       266265 :             if (state.dataGlobal->MetersHaveBeenInitialized) {
    4779              : 
    4780       203950 :                 elecLoad +=
    4781       203950 :                     GetInstantMeterValue(state, wiredMeterPtrs_[meterNum], OutputProcessor::TimeStepType::Zone) / state.dataGlobal->TimeStepZoneSec +
    4782       203950 :                     GetInstantMeterValue(state, wiredMeterPtrs_[meterNum], OutputProcessor::TimeStepType::System) /
    4783       203950 :                         (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       203950 :                 pastElecLoad += GetCurrentMeterValue(state, wiredMeterPtrs_[meterNum]) / state.dataGlobal->TimeStepZoneSec;
    4787              :             } else {
    4788        62315 :                 elecLoad = 0.0;
    4789        62315 :                 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       266265 :             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       138120 :         powerOut_ = elecLoad; // the metered value is transformer's output in PowerInFromGrid mode
    4804       138120 :         break;
    4805              :     }
    4806         8508 :     case TransformerUse::PowerOutFromBldgToGrid: {
    4807         8508 :         powerIn_ = surplusPowerOutFromLoadCenters;
    4808         8508 :         elecLoad = surplusPowerOutFromLoadCenters; // TODO this is input but should be output with the losses, but we don't have them yet.
    4809         8508 :         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       146628 :     if (ratedCapacity_ > 0.0 && availSched_->getCurrentVal() > 0.0) {
    4827              : 
    4828       146628 :         Real64 pUL = elecLoad / ratedCapacity_;
    4829              : 
    4830       146628 :         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       146628 :         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       146628 :         Real64 tempChange = std::pow(pUL, 1.6) * tempRise_;
    4847       146628 :         Real64 ambTemp = 20.0;
    4848       146628 :         if (heatLossesDestination_ == ThermalLossDestination::ZoneGains) {
    4849              : 
    4850            0 :             ambTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum_).MAT;
    4851              :         } else {
    4852       146628 :             ambTemp = 20.0;
    4853              :         }
    4854              : 
    4855       146628 :         Real64 resRef = factorTempCoeff_ + tempRise_ + ambTempRef;
    4856       146628 :         Real64 resSpecified = factorTempCoeff_ + tempChange + ambTemp;
    4857       146628 :         Real64 resRatio = resSpecified / resRef;
    4858       146628 :         Real64 factorTempCorr = (1.0 - eddyFrac_) * resRatio + eddyFrac_ * (1.0 / resRatio);
    4859              : 
    4860       146628 :         loadLossRate_ = ratedLL_ * pow_2(pUL) * factorTempCorr;
    4861       146628 :         noLoadLossRate_ = ratedNL_;
    4862              :     } else { // Transformer is not available.
    4863            0 :         loadLossRate_ = 0.0;
    4864            0 :         noLoadLossRate_ = 0.0;
    4865              :     }
    4866              : 
    4867       146628 :     totalLossRate_ = loadLossRate_ + noLoadLossRate_;
    4868              : 
    4869       146628 :     switch (usageMode_) {
    4870       138120 :     case TransformerUse::PowerInFromGrid: {
    4871       138120 :         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       138120 :         if (considerLosses_) {
    4877       138120 :             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       138120 :         break;
    4887              :     }
    4888              : 
    4889         8508 :     case TransformerUse::PowerOutFromBldgToGrid:
    4890              :     case TransformerUse::PowerBetweenLoadCenterAndBldg: {
    4891         8508 :         powerOut_ = elecLoad - totalLossRate_;
    4892              : 
    4893         8508 :         if (powerOut_ < 0) {
    4894         6531 :             powerOut_ = 0.0;
    4895              :         }
    4896              : 
    4897         8508 :         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         8508 :         elecUseMeteredUtilityLosses_ = 0.0;
    4902         8508 :         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       146628 :     if (powerIn_ <= 0) {
    4914         6531 :         efficiency_ = 1.0; // Set to something reasonable to avoid a divide by zero error
    4915              :     } else {
    4916       140097 :         efficiency_ = powerOut_ / powerIn_;
    4917              :     }
    4918       146628 :     noLoadLossEnergy_ = noLoadLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4919       146628 :     loadLossEnergy_ = loadLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4920              : 
    4921       146628 :     energyIn_ = powerIn_ * state.dataHVACGlobal->TimeStepSysSec;
    4922       146628 :     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       146628 :     thermalLossRate_ = powerIn_ - powerOut_;
    4927       146628 :     thermalLossEnergy_ = thermalLossRate_ * state.dataHVACGlobal->TimeStepSysSec;
    4928              : 
    4929       146628 :     if (zoneNum_ > 0) { // set values for zone heat gains
    4930            0 :         qdotConvZone_ = (1.0 - zoneRadFrac_) * thermalLossRate_;
    4931            0 :         qdotRadZone_ = (zoneRadFrac_)*thermalLossRate_;
    4932              :     }
    4933       146628 : }
    4934              : 
    4935           12 : void ElectricTransformer::setupMeterIndices(EnergyPlusData &state)
    4936              : {
    4937           12 :     if (usageMode_ == TransformerUse::PowerInFromGrid) {
    4938           35 :         for (std::size_t meterNum = 0; meterNum < wiredMeterNames_.size(); ++meterNum) {
    4939              : 
    4940           23 :             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           23 :             auto *meter = state.dataOutputProcessor->meters[wiredMeterPtrs_[meterNum]];
    4947           23 :             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           12 : }
    4955              : 
    4956          135 : void ElectricTransformer::reinitAtBeginEnvironment()
    4957              : {
    4958          135 :     efficiency_ = 0.0;
    4959          135 :     powerIn_ = 0.0;
    4960          135 :     energyIn_ = 0.0;
    4961          135 :     powerOut_ = 0.0;
    4962          135 :     energyOut_ = 0.0;
    4963          135 :     noLoadLossRate_ = 0.0;
    4964          135 :     noLoadLossEnergy_ = 0.0;
    4965          135 :     loadLossRate_ = 0.0;
    4966          135 :     loadLossEnergy_ = 0.0;
    4967          135 :     thermalLossRate_ = 0.0;
    4968          135 :     thermalLossEnergy_ = 0.0;
    4969          135 :     elecUseMeteredUtilityLosses_ = 0.0;
    4970          135 :     powerConversionMeteredLosses_ = 0.0;
    4971          135 :     qdotConvZone_ = 0.0;
    4972          135 :     qdotRadZone_ = 0.0;
    4973          135 : }
    4974              : 
    4975          122 : void ElectricTransformer::reinitZoneGainsAtBeginEnvironment()
    4976              : {
    4977          122 :     qdotConvZone_ = 0.0;
    4978          122 :     qdotRadZone_ = 0.0;
    4979          122 : }
    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