LCOV - code coverage report
Current view: top level - EnergyPlus - ElectricPowerServiceManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 2084 3028 68.8 %
Date: 2024-08-24 18:31:18 Functions: 64 75 85.3 %

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

Generated by: LCOV version 1.14