LCOV - code coverage report
Current view: top level - EnergyPlus - ElectricPowerServiceManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 46.9 % 2808 1316
Test Date: 2025-05-22 16:09:37 Functions: 68.0 % 75 51

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

Generated by: LCOV version 2.0-1