LCOV - code coverage report
Current view: top level - EnergyPlus - ElectricPowerServiceManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 2054 2823 72.8 %
Date: 2023-01-17 19:17:23 Functions: 66 82 80.5 %

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

Generated by: LCOV version 1.13