LCOV - code coverage report
Current view: top level - EnergyPlus - PlantLoopHeatPumpEIR.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 70.1 % 1947 1365
Test Date: 2025-06-02 12:03:30 Functions: 90.0 % 50 45

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ headers
      49              : #include <algorithm>
      50              : #include <string>
      51              : #include <utility>
      52              : #include <vector>
      53              : 
      54              : // EnergyPlus headers
      55              : #include <EnergyPlus/Autosizing/Base.hh>
      56              : #include <EnergyPlus/BranchNodeConnections.hh>
      57              : #include <EnergyPlus/CurveManager.hh>
      58              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59              : #include <EnergyPlus/DataEnvironment.hh>
      60              : #include <EnergyPlus/DataHVACGlobals.hh>
      61              : #include <EnergyPlus/DataIPShortCuts.hh>
      62              : #include <EnergyPlus/DataLoopNode.hh>
      63              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      64              : #include <EnergyPlus/DataSizing.hh>
      65              : #include <EnergyPlus/FluidProperties.hh>
      66              : #include <EnergyPlus/General.hh>
      67              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      68              : #include <EnergyPlus/NodeInputManager.hh>
      69              : #include <EnergyPlus/OutAirNodeManager.hh>
      70              : #include <EnergyPlus/OutputProcessor.hh>
      71              : #include <EnergyPlus/OutputReportPredefined.hh>
      72              : #include <EnergyPlus/Plant/DataPlant.hh>
      73              : #include <EnergyPlus/PlantComponent.hh>
      74              : #include <EnergyPlus/PlantLoopHeatPumpEIR.hh>
      75              : #include <EnergyPlus/PlantUtilities.hh>
      76              : #include <EnergyPlus/Psychrometrics.hh>
      77              : #include <EnergyPlus/UtilityRoutines.hh>
      78              : 
      79              : namespace EnergyPlus::EIRPlantLoopHeatPumps {
      80              : 
      81              : constexpr Real64 Fahrenheit2Celsius(Real64 F)
      82              : {
      83              :     return (F - 32.0) * 5.0 / 9.0;
      84              : }
      85              : 
      86           45 : void EIRPlantLoopHeatPump::simulate(
      87              :     EnergyPlusData &state, const EnergyPlus::PlantLocation &calledFromLocation, bool const FirstHVACIteration, Real64 &CurLoad, bool const RunFlag)
      88              : {
      89              : 
      90              :     // Call initialize to set flow rates, run flag, and entering temperatures
      91           45 :     this->running = RunFlag;
      92              : 
      93           45 :     this->loadSideInletTemp = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
      94           45 :     this->sourceSideInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
      95           45 :     if (this->heatRecoveryAvailable) {
      96            6 :         this->heatRecoveryInletTemp = state.dataLoopNodes->Node(this->heatRecoveryNodes.inlet).Temp;
      97              :     }
      98              : 
      99           45 :     if (this->waterSource) {
     100           11 :         this->setOperatingFlowRatesWSHP(state, FirstHVACIteration);
     101           11 :         if (calledFromLocation.loopNum == this->sourceSidePlantLoc.loopNum) { // condenser side
     102            2 :             Real64 sourceQdotArg = 0.0;                                       // pass negative if heat pump heating
     103            2 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
     104            0 :                 sourceQdotArg = this->sourceSideHeatTransfer * DataPrecisionGlobals::constant_minusone;
     105              :             } else {
     106            2 :                 sourceQdotArg = this->sourceSideHeatTransfer;
     107              :             }
     108            2 :             PlantUtilities::UpdateChillerComponentCondenserSide(state,
     109              :                                                                 this->sourceSidePlantLoc.loopNum,
     110              :                                                                 this->sourceSidePlantLoc.loopSideNum,
     111              :                                                                 this->EIRHPType,
     112              :                                                                 this->sourceSideNodes.inlet,
     113              :                                                                 this->sourceSideNodes.outlet,
     114              :                                                                 this->sourceSideHeatTransfer,
     115              :                                                                 this->sourceSideInletTemp,
     116              :                                                                 this->sourceSideOutletTemp,
     117              :                                                                 this->sourceSideMassFlowRate,
     118              :                                                                 FirstHVACIteration);
     119            2 :             return;
     120              :         }
     121           34 :     } else if (this->airSource) {
     122           34 :         this->setHeatRecoveryOperatingStatusASHP(state, FirstHVACIteration);
     123           34 :         this->setOperatingFlowRatesASHP(state, FirstHVACIteration, CurLoad);
     124              : 
     125           34 :         if (calledFromLocation.loopNum == this->heatRecoveryPlantLoc.loopNum) {
     126            0 :             if (this->heatRecoveryAvailable) {
     127            0 :                 PlantUtilities::UpdateChillerComponentCondenserSide(state,
     128              :                                                                     this->heatRecoveryPlantLoc.loopNum,
     129              :                                                                     this->heatRecoveryPlantLoc.loopSideNum,
     130              :                                                                     this->EIRHPType,
     131              :                                                                     this->heatRecoveryNodes.inlet,
     132              :                                                                     this->heatRecoveryNodes.outlet,
     133              :                                                                     this->heatRecoveryRate,
     134              :                                                                     this->heatRecoveryInletTemp,
     135              :                                                                     this->heatRecoveryOutletTemp,
     136              :                                                                     this->heatRecoveryMassFlowRate,
     137              :                                                                     FirstHVACIteration);
     138              :             }
     139              :         }
     140              :     }
     141              : 
     142           43 :     if (this->running) {
     143           36 :         if (this->sysControlType == ControlType::Setpoint) {
     144            4 :             Real64 leavingSetpoint = state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
     145            4 :             Real64 CurSpecHeat = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, loadSideInletTemp, "EIRPlantLoopHeatPump::simulate");
     146            4 :             Real64 controlLoad = this->loadSideMassFlowRate * CurSpecHeat * (leavingSetpoint - loadSideInletTemp);
     147              : 
     148            4 :             this->doPhysics(state, controlLoad);
     149              :         } else {
     150           32 :             this->doPhysics(state, CurLoad);
     151              :         }
     152              :     } else {
     153            7 :         this->resetReportingVariables();
     154              :     }
     155              : 
     156              :     // update report variables and nodes
     157           43 :     this->report(state);
     158              : }
     159              : 
     160           25 : Real64 EIRPlantLoopHeatPump::getLoadSideOutletSetPointTemp(EnergyPlusData &state) const
     161              : {
     162           25 :     if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     163           19 :         if (this->loadSidePlantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
     164              :             // there will be a valid set-point on outlet
     165           15 :             return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
     166              :         } else { // use plant loop overall set-point
     167            4 :             return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPoint;
     168              :         }
     169            6 :     } else if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
     170            6 :         if (this->loadSidePlantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
     171              :             // there will be a valid set-point on outlet
     172            2 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
     173            1 :                 return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointHi;
     174              :             } else {
     175            1 :                 return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointLo;
     176              :             }
     177              :         } else { // use plant loop overall set-point
     178            4 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
     179            3 :                 return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPointHi;
     180              :             } else {
     181            1 :                 return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPointLo;
     182              :             }
     183              :         }
     184              :     } else {
     185              :         // there's no other enums for loop demand calcs, so I don't have a reasonable unit test for these
     186              :         // lines, they simply should not be able to get here.  But a fatal is here anyway just in case,
     187              :         // and the lines are excluded from coverage.
     188              :         ShowFatalError(state, "Unsupported loop demand calculation scheme in EIR heat pump"); // LCOV_EXCL_LINE
     189              :         return -999; // not actually returned with Fatal Error call above  // LCOV_EXCL_LINE
     190              :     }
     191              : }
     192              : 
     193            8 : void EIRPlantLoopHeatPump::resetReportingVariables()
     194              : {
     195            8 :     this->loadSideHeatTransfer = 0.0;
     196            8 :     this->loadSideEnergy = 0.0;
     197            8 :     this->loadSideOutletTemp = this->loadSideInletTemp;
     198            8 :     this->powerUsage = 0.0;
     199            8 :     this->powerEnergy = 0.0;
     200            8 :     this->sourceSideHeatTransfer = 0.0;
     201            8 :     this->sourceSideOutletTemp = this->sourceSideInletTemp;
     202            8 :     this->sourceSideEnergy = 0.0;
     203            8 :     this->defrostEnergyRate = 0.0;
     204            8 :     this->defrostEnergy = 0.0;
     205            8 :     this->loadDueToDefrost = 0.0;
     206            8 :     this->fractionalDefrostTime = 0.0;
     207            8 :     this->partLoadRatio = 0.0;
     208            8 :     this->cyclingRatio = 0.0;
     209            8 :     this->defrostPowerMultiplier = 1.0;
     210            8 :     this->heatRecoveryRate = 0.0;
     211            8 :     this->heatRecoveryEnergy = 0.0;
     212            8 :     this->heatRecoveryMassFlowRate = 0.0;
     213            8 :     this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
     214            8 :     this->heatRecoveryIsActive = false;
     215            8 :     this->heatRecoveryOperatingStatus = 0;
     216            8 :     this->thermosiphonStatus = 0;
     217            8 : }
     218              : 
     219           18 : void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool FirstHVACIteration)
     220              : {
     221           18 :     if (!this->running) {
     222            7 :         this->loadSideMassFlowRate = 0.0;
     223            7 :         this->sourceSideMassFlowRate = 0.0;
     224              : 
     225            7 :         PlantUtilities::SetComponentFlowRate(
     226            7 :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     227            7 :         PlantUtilities::SetComponentFlowRate(
     228            7 :             state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     229            7 :         PlantUtilities::PullCompInterconnectTrigger(state,
     230            7 :                                                     this->loadSidePlantLoc,
     231            7 :                                                     this->condMassFlowRateTriggerIndex,
     232            7 :                                                     this->sourceSidePlantLoc,
     233              :                                                     DataPlant::CriteriaType::MassFlowRate,
     234              :                                                     this->sourceSideMassFlowRate);
     235              :         // Set flows if the heat pump is running
     236              :     } else { // the heat pump must run
     237              :         // apply min/max operating limits based on source side entering fluid temperature
     238           11 :         if (this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) {
     239            0 :             this->loadSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->loadSideNodes.inlet).MassFlowRate : 0.0;
     240            0 :             this->sourceSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->sourceSideNodes.inlet).MassFlowRate : 0.0;
     241            0 :             this->running = false;
     242              :         } else {
     243           11 :             this->loadSideMassFlowRate =
     244           11 :                 (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->loadSideNodes.inlet).MassFlowRate : this->loadSideDesignMassFlowRate;
     245           11 :             this->sourceSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->sourceSideNodes.inlet).MassFlowRate
     246              :                                                                         : this->sourceSideDesignMassFlowRate;
     247              : 
     248           11 :             if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
     249            0 :                 if ((this->loadVSBranchPump || this->loadVSLoopPump) && !this->heatRecoveryHeatPump) {
     250            0 :                     this->loadSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
     251            0 :                     if (this->loadVSBranchPump) {
     252            0 :                         this->loadSideMassFlowRate = std::max(this->loadSideMassFlowRate, this->loadVSPumpMinLimitMassFlow);
     253              :                     }
     254              :                 }
     255            0 :                 if ((this->sourceVSBranchPump || this->sourceVSLoopPump) && !this->heatRecoveryHeatPump) {
     256            0 :                     this->sourceSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
     257            0 :                     if (this->sourceVSBranchPump) {
     258            0 :                         this->sourceSideMassFlowRate = std::max(this->sourceSideMassFlowRate, this->sourceVSPumpMinLimitMassFlow);
     259              :                     }
     260              :                 }
     261              :             }
     262              : 
     263           11 :             PlantUtilities::SetComponentFlowRate(
     264           11 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     265           11 :             PlantUtilities::SetComponentFlowRate(
     266           11 :                 state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     267              :         }
     268              : 
     269              :         // if there's no flow in one, try to turn the entire heat pump off
     270           11 :         if (this->loadSideMassFlowRate <= 0.0 || this->sourceSideMassFlowRate <= 0.0) {
     271            3 :             this->loadSideMassFlowRate = 0.0;
     272            3 :             this->sourceSideMassFlowRate = 0.0;
     273            3 :             this->running = false;
     274            3 :             PlantUtilities::SetComponentFlowRate(
     275            3 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     276            3 :             PlantUtilities::SetComponentFlowRate(
     277            3 :                 state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     278              :         }
     279           11 :         PlantUtilities::PullCompInterconnectTrigger(state,
     280           11 :                                                     this->loadSidePlantLoc,
     281           11 :                                                     this->condMassFlowRateTriggerIndex,
     282           11 :                                                     this->sourceSidePlantLoc,
     283              :                                                     DataPlant::CriteriaType::MassFlowRate,
     284              :                                                     this->sourceSideMassFlowRate);
     285              :     }
     286           18 : }
     287              : 
     288           24 : void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, Real64 const currentLoad)
     289              : {
     290           24 :     if (!this->running) {
     291            6 :         this->loadSideMassFlowRate = 0.0;
     292            6 :         this->sourceSideMassFlowRate = 0.0;
     293            6 :         PlantUtilities::SetComponentFlowRate(
     294            6 :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     295            6 :         if (this->heatRecoveryAvailable) {
     296              :             // set the HR flow to zero if the heat pump is off
     297            2 :             this->heatRecoveryMassFlowRate = 0.0;
     298            2 :             PlantUtilities::SetComponentFlowRate(
     299            2 :                 state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
     300              :         }
     301              :         // Set flows if the heat pump is running
     302              :     } else { // the heat pump must run
     303              :         // apply min/max operating limits based on source side entering fluid temperature
     304           18 :         if ((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) &&
     305            0 :             !this->heatRecoveryIsActive) {
     306            0 :             this->loadSideMassFlowRate = 0.0;
     307            0 :             this->sourceSideMassFlowRate = 0.0;
     308            0 :             this->running = false;
     309            0 :             PlantUtilities::SetComponentFlowRate(
     310            0 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     311            0 :             if (this->heatRecoveryAvailable) {
     312            0 :                 this->heatRecoveryMassFlowRate = 0.0;
     313            0 :                 PlantUtilities::SetComponentFlowRate(
     314            0 :                     state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
     315              :             }
     316              :         } else {
     317           18 :             this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
     318           18 :             this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
     319              : 
     320           18 :             if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
     321            0 :                 if (this->loadVSBranchPump || this->loadVSLoopPump) {
     322            0 :                     this->loadSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
     323            0 :                     if (this->loadVSBranchPump) {
     324            0 :                         this->loadSideMassFlowRate = std::max(this->loadSideMassFlowRate, this->loadVSPumpMinLimitMassFlow);
     325              :                     }
     326              :                 }
     327              :             }
     328              : 
     329           18 :             PlantUtilities::SetComponentFlowRate(
     330           18 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     331              : 
     332           18 :             if (this->heatRecoveryIsActive) {
     333            4 :                 this->heatRecoveryMassFlowRate = this->heatRecoveryDesignMassFlowRate;
     334            4 :                 PlantUtilities::SetComponentFlowRate(
     335            4 :                     state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
     336              :             }
     337              :         }
     338              : 
     339              :         // if there's no flow in one, try to turn the entire heat pump off
     340           18 :         if (this->loadSideMassFlowRate <= 0.0) {
     341            2 :             this->loadSideMassFlowRate = 0.0;
     342            2 :             this->sourceSideMassFlowRate = 0.0;
     343            2 :             this->running = false;
     344            2 :             PlantUtilities::SetComponentFlowRate(
     345            2 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     346              :             // if heat recovery is connected to plant loop
     347            2 :             if (this->heatRecoveryAvailable) {
     348            0 :                 this->heatRecoveryMassFlowRate = 0.0;
     349            0 :                 PlantUtilities::SetComponentFlowRate(
     350            0 :                     state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
     351              :             }
     352              :         }
     353           18 :         if (this->heatRecoveryAvailable) {
     354            4 :             PlantUtilities::PullCompInterconnectTrigger(state,
     355            4 :                                                         this->loadSidePlantLoc,
     356            4 :                                                         this->condMassFlowRateTriggerIndex,
     357            4 :                                                         this->heatRecoveryPlantLoc,
     358              :                                                         DataPlant::CriteriaType::MassFlowRate,
     359              :                                                         this->heatRecoveryMassFlowRate);
     360              :         }
     361              :     }
     362           24 : }
     363              : 
     364           25 : void EIRFuelFiredHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, Real64 const currentLoad)
     365              : {
     366           25 :     if (!this->running) {
     367            5 :         this->loadSideMassFlowRate = 0.0;
     368            5 :         this->sourceSideMassFlowRate = 0.0;
     369            5 :         PlantUtilities::SetComponentFlowRate(
     370            5 :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     371            5 :         if (this->heatRecoveryAvailable) {
     372              :             // set the HR flow to zero if the heat pump is off
     373            0 :             this->heatRecoveryMassFlowRate = 0.0;
     374            0 :             PlantUtilities::SetComponentFlowRate(
     375            0 :                 state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
     376              :         }
     377              :     } else { // the heat pump must run
     378           20 :         this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
     379           20 :         this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
     380              : 
     381           20 :         if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::LeavingSetpointModulated) {
     382            1 :             auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
     383            1 :             auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
     384            1 :             Real64 FFHPDeltaTemp = 0.0;
     385            1 :             Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, thisInletNode.Temp, "PLFFHPEIR::simulate()");
     386            1 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) {
     387            1 :                 if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     388            1 :                     FFHPDeltaTemp = thisOutletNode.TempSetPoint - thisInletNode.Temp;
     389              :                 } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
     390            0 :                     FFHPDeltaTemp = thisOutletNode.TempSetPointLo - thisInletNode.Temp;
     391              :                 }
     392            1 :                 this->loadSideOutletTemp = FFHPDeltaTemp + thisInletNode.Temp;
     393            1 :                 if ((FFHPDeltaTemp > 0.0) && currentLoad > 0.0) {
     394            1 :                     this->loadSideMassFlowRate = currentLoad / (CpLoad * FFHPDeltaTemp);
     395            1 :                     this->loadSideMassFlowRate = min(this->loadSideDesignMassFlowRate, this->loadSideMassFlowRate);
     396              :                 } else {
     397            0 :                     this->loadSideOutletTemp = thisInletNode.Temp;
     398            0 :                     this->loadSideMassFlowRate = 0.0;
     399              :                 }
     400            0 :             } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
     401            0 :                 if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     402            0 :                     FFHPDeltaTemp = thisInletNode.Temp - thisOutletNode.TempSetPoint;
     403              :                 } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
     404            0 :                     FFHPDeltaTemp = thisInletNode.Temp - thisOutletNode.TempSetPointHi;
     405              :                 }
     406            0 :                 this->loadSideOutletTemp = thisInletNode.Temp - FFHPDeltaTemp;
     407            0 :                 if ((FFHPDeltaTemp > 0.0) && std::abs(currentLoad) > 0.0) {
     408            0 :                     this->loadSideMassFlowRate = std::abs(currentLoad) / (CpLoad * FFHPDeltaTemp);
     409            0 :                     this->loadSideMassFlowRate = min(this->loadSideDesignMassFlowRate, this->loadSideMassFlowRate);
     410              :                 } else {
     411            0 :                     this->loadSideOutletTemp = thisInletNode.Temp;
     412            0 :                     this->loadSideMassFlowRate = 0.0;
     413              :                 }
     414              :             }
     415              :         }
     416           20 :         PlantUtilities::SetComponentFlowRate(
     417           20 :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     418              : 
     419              :         // if there's no flow in one, try to turn the entire heat pump off
     420           20 :         if (this->loadSideMassFlowRate <= 0.0) {
     421            2 :             this->loadSideMassFlowRate = 0.0;
     422            2 :             this->sourceSideMassFlowRate = 0.0;
     423            2 :             this->running = false;
     424            2 :             PlantUtilities::SetComponentFlowRate(
     425            2 :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     426              :         }
     427              :     }
     428           25 : }
     429              : 
     430           22 : void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
     431              : {
     432              :     // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
     433              :     // I'm not sure we can count on that so we will do one check here to make sure we don't calculate things badly
     434           22 :     if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling && currentLoad >= 0.0) ||
     435           22 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && currentLoad <= 0.0)) {
     436            3 :         this->resetReportingVariables();
     437            3 :         return;
     438              :     }
     439              : 
     440              :     // dispatch to specific physics calculations based on the heat pump type
     441           19 :     if (this->waterSource) {
     442            8 :         this->doPhysicsWSHP(state, currentLoad);
     443           11 :     } else if (this->airSource) {
     444           11 :         this->doPhysicsASHP(state, currentLoad);
     445              :     }
     446              : }
     447              : 
     448            8 : void EIRPlantLoopHeatPump::doPhysicsWSHP(EnergyPlusData &state, Real64 currentLoad)
     449              : {
     450              : 
     451              :     // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
     452              : 
     453            8 :     Real64 availableCapacity = this->referenceCapacity;
     454            8 :     Real64 partLoadRatio = 0.0;
     455              : 
     456            8 :     this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio);
     457            8 :     this->setPartLoadAndCyclingRatio(state, partLoadRatio);
     458              : 
     459              :     // evaluate the actual current operating load side heat transfer rate
     460            8 :     this->calcLoadSideHeatTransfer(state, availableCapacity);
     461              : 
     462              :     // no defrost calculation for WSHP
     463              :     // calculate power usage from EIR curves
     464            8 :     this->calcPowerUsage(state);
     465              : 
     466              :     // evaluate the source side heat transfer rate
     467            8 :     this->calcSourceSideHeatTransferWSHP(state);
     468            8 : }
     469              : 
     470           11 : void EIRPlantLoopHeatPump::doPhysicsASHP(EnergyPlusData &state, Real64 currentLoad)
     471              : {
     472              :     // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
     473              : 
     474           11 :     Real64 availableCapacity = this->referenceCapacity;
     475           11 :     Real64 partLoadRatio = 0.0;
     476              : 
     477           11 :     this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio);
     478           11 :     this->setPartLoadAndCyclingRatio(state, partLoadRatio);
     479              : 
     480              :     // do defrost calculation if applicable
     481           11 :     this->doDefrost(state, availableCapacity);
     482              : 
     483              :     // evaluate the actual current operating load side heat transfer rate
     484           11 :     this->calcLoadSideHeatTransfer(state, availableCapacity);
     485              : 
     486              :     //  calculate power usage from EIR curves
     487           11 :     this->calcPowerUsage(state);
     488              : 
     489           11 :     if (this->heatRecoveryIsActive) {
     490              :         // evaluate the heat recovery side heat transfer rate
     491            4 :         this->calcHeatRecoveryHeatTransferASHP(state);
     492              :     } else {
     493              :         // evaluate the source side heat transfer rate
     494            7 :         this->calcSourceSideHeatTransferASHP(state);
     495              :     }
     496           11 : }
     497              : 
     498           19 : void EIRPlantLoopHeatPump::calcAvailableCapacity(EnergyPlusData &state, Real64 const currentLoad, Real64 &availableCapacity, Real64 &partLoadRatio)
     499              : {
     500              :     // get setpoint on the load side outlet
     501           19 :     Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
     502           19 :     Real64 originalLoadSideOutletSPTemp = loadSideOutletSetpointTemp;
     503              : 
     504              :     // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
     505              : 
     506           19 :     Real64 capacityModifierFuncTemp = 1.0;
     507           19 :     bool waterTempExceeded = false;
     508              : 
     509              :     // evaluate capacity modifier curve and determine load side heat transfer
     510              :     // any adjustment to outlet water temp set point requires some form of iteration
     511           19 :     for (int loop = 0; loop < 2; ++loop) {
     512              : 
     513           19 :         if (this->heatRecoveryIsActive) {
     514            4 :             if (this->heatRecoveryCapFTempCurveIndex > 0) {
     515            0 :                 capacityModifierFuncTemp =
     516            0 :                     Curve::CurveValue(state, this->heatRecoveryCapFTempCurveIndex, loadSideOutletSetpointTemp, this->heatRecoveryInletTemp);
     517              :             } else {
     518            4 :                 capacityModifierFuncTemp =
     519            4 :                     Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->heatRecoveryInletTemp);
     520              :             }
     521            4 :             availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
     522              :         } else {
     523           15 :             capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->sourceSideInletTemp);
     524           15 :             availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
     525              :             // apply air source HP dry air heating capacity correction
     526           15 :             availableCapacity *= heatingCapacityModifierASHP(state);
     527              :         }
     528              : 
     529           19 :         if (availableCapacity > 0) {
     530           19 :             partLoadRatio = std::clamp(std::abs(currentLoad) / availableCapacity, 0.0, 1.0);
     531              :         }
     532              : 
     533           19 :         if (this->minSupplyWaterTempCurveIndex > 0) {
     534            0 :             Real64 minWaterTemp = Curve::CurveValue(state, this->minSupplyWaterTempCurveIndex, state.dataEnvrn->OutDryBulbTemp);
     535            0 :             if (loadSideOutletSetpointTemp < minWaterTemp) {
     536            0 :                 loadSideOutletSetpointTemp = originalLoadSideOutletSPTemp + (1.0 - partLoadRatio) * (minWaterTemp - originalLoadSideOutletSPTemp);
     537            0 :                 waterTempExceeded = true;
     538              :             }
     539              :         }
     540           19 :         if (this->maxSupplyWaterTempCurveIndex > 0) {
     541            0 :             Real64 maxWaterTemp = Curve::CurveValue(state, this->maxSupplyWaterTempCurveIndex, state.dataEnvrn->OutDryBulbTemp);
     542            0 :             if (loadSideOutletSetpointTemp > maxWaterTemp) {
     543            0 :                 loadSideOutletSetpointTemp = maxWaterTemp + (1.0 - partLoadRatio) * (originalLoadSideOutletSPTemp - maxWaterTemp);
     544            0 :                 waterTempExceeded = true;
     545              :             }
     546              :         }
     547           19 :         if (this->heatRecoveryHeatPump) {
     548            2 :             this->calcLoadSideHeatTransfer(state, availableCapacity);
     549            2 :             this->calcPowerUsage(state);
     550            2 :             Real64 sourceSideHeatTransfer = this->calcQsource(availableCapacity * partLoadRatio, this->powerUsage);
     551              :             // check to see if souce side outlet temp exceeds limit and reduce PLR if necessary
     552            2 :             Real64 const CpSrc = this->sourceSidePlantLoc.loop->glycol->getSpecificHeat(
     553              :                 state, this->sourceSideInletTemp, "EIRPlantLoopHeatPump::calcLoadSideHeatTransfer()");
     554            2 :             Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
     555            2 :             Real64 const tempSourceOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, sourceSideHeatTransfer / sourceMCp);
     556            2 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && tempSourceOutletTemp < this->minSourceTempLimit) {
     557            0 :                 partLoadRatio *= (this->sourceSideInletTemp - this->minSourceTempLimit) / (this->sourceSideInletTemp - tempSourceOutletTemp);
     558            2 :             } else if (tempSourceOutletTemp > this->maxSourceTempLimit) {
     559            1 :                 partLoadRatio *= (this->maxSourceTempLimit - this->sourceSideInletTemp) / (tempSourceOutletTemp - this->sourceSideInletTemp);
     560              :             }
     561              :         }
     562           19 :         if (!waterTempExceeded) {
     563           19 :             break;
     564              :         }
     565              :     }
     566              : 
     567              :     // check the curve values, reset to zero if negative
     568           19 :     if (this->heatRecoveryIsActive && this->heatRecoveryCapFTempCurveIndex > 0) {
     569            0 :         this->heatRecoveryCapModFTCurveCheck(state, loadSideOutletSetpointTemp, capacityModifierFuncTemp);
     570              :     } else {
     571           19 :         this->capModFTCurveCheck(state, loadSideOutletSetpointTemp, capacityModifierFuncTemp);
     572              :     }
     573           19 : }
     574              : 
     575           15 : Real64 EIRPlantLoopHeatPump::heatingCapacityModifierASHP(EnergyPlusData &state) const
     576              : {
     577           15 :     Real64 constexpr RH90 = 90.0;
     578           15 :     Real64 constexpr RH60 = 60.0;
     579           15 :     Real64 constexpr rangeRH = 30.0;
     580              : 
     581              :     // apply heating mode dry outdoor (evaporator) coil correction factor for air-cooled equipment
     582           15 :     if (this->capacityDryAirCurveIndex > 0 && this->airSource && state.dataEnvrn->OutRelHum < RH90) { // above 90% RH yields full capacity
     583            0 :         Real64 dryCorrectionFactor = std::min(1.0, Curve::CurveValue(state, this->capacityDryAirCurveIndex, state.dataEnvrn->OutDryBulbTemp));
     584            0 :         if (state.dataEnvrn->OutRelHum <= RH60) {
     585              :             // dry heating capacity correction factor is a function of outdoor dry-bulb temperature
     586            0 :             return dryCorrectionFactor;
     587              :         } else {
     588              :             // interpolation of heating capacity between wet and dry is based on outdoor relative humidity over 60%-90% range
     589            0 :             Real64 semiDryFactor = dryCorrectionFactor + (1.0 - dryCorrectionFactor) * (1.0 - ((RH90 - state.dataEnvrn->OutRelHum) / rangeRH));
     590            0 :             return semiDryFactor;
     591              :         }
     592              :     } else {
     593              :         // no correction needed, use full capacity
     594           15 :         return 1.0;
     595              :     }
     596              : }
     597              : 
     598           19 : void EIRPlantLoopHeatPump::setPartLoadAndCyclingRatio([[maybe_unused]] EnergyPlusData &state, Real64 &partLoadRatio)
     599              : {
     600              :     // Initialize cycling ratio to 1.0
     601           19 :     Real64 cyclingRatio = 1.0;
     602              : 
     603              :     // Check if part load ratio is below the minimum threshold
     604           19 :     if (partLoadRatio < this->minimumPLR) {
     605              :         // Adjust cycling ratio and set part load ratio to minimum
     606            0 :         cyclingRatio = partLoadRatio / this->minimumPLR;
     607            0 :         partLoadRatio = this->minimumPLR;
     608              :     }
     609              : 
     610              :     // update class member variables
     611           19 :     this->partLoadRatio = partLoadRatio;
     612           19 :     this->cyclingRatio = cyclingRatio;
     613           19 : }
     614              : 
     615           21 : void EIRPlantLoopHeatPump::calcLoadSideHeatTransfer(EnergyPlusData &state, Real64 const availableCapacity)
     616              : {
     617              :     // evaluate the actual current operating load side heat transfer rate
     618           21 :     Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(
     619           21 :         state, state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp, "EIRPlantLoopHeatPump::calcLoadSideHeatTransfer()");
     620              : 
     621           21 :     Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio;
     622           21 :     this->loadSideHeatTransfer = availableCapacity * operatingPLR;
     623              : 
     624              :     // calculate load side outlet conditions
     625           21 :     Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
     626           21 :     this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
     627              : 
     628              :     // now what to do here if outlet water temp exceeds limit based on HW supply temp limit curves?
     629              :     // currentLoad will be met and there should? be some adjustment based on outlet water temp limit?
     630           21 : }
     631              : 
     632           21 : void EIRPlantLoopHeatPump::calcPowerUsage(EnergyPlusData &state)
     633              : {
     634              :     // calculate power usage from EIR curves
     635           21 :     Real64 eirModifierFuncTemp = 0.0;
     636           21 :     if (this->airSource && this->heatRecoveryIsActive) {
     637            4 :         if (this->heatRecoveryEIRFTempCurveIndex > 0) {
     638            0 :             eirModifierFuncTemp =
     639            0 :                 Curve::CurveValue(state, this->heatRecoveryEIRFTempCurveIndex, this->loadSideOutletTemp, this->heatRecoveryInletTemp);
     640              :             // check cap func of temp curve value and reset to zero if negative
     641            0 :             this->heatRecoveryEIRModCurveCheck(state, eirModifierFuncTemp);
     642              :         } else {
     643            4 :             eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
     644              :             // check cap func of temp curve value and reset to zero if negative
     645            4 :             this->eirModCurveCheck(state, eirModifierFuncTemp);
     646              :         }
     647              :     } else {
     648           17 :         eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
     649              :         // check curves value and resets to zero if negative
     650           17 :         this->eirModCurveCheck(state, eirModifierFuncTemp);
     651              :     }
     652           21 :     Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, this->partLoadRatio);
     653              :     // check EIR func of PLR curve value and resets to zero if negative
     654           21 :     this->eirModFPLRCurveCheck(state, eirModifierFuncPLR);
     655              : 
     656              :     // compute power usage
     657           21 :     if (this->thermosiphonDisabled(state)) {
     658           20 :         this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp *
     659           20 :                            this->defrostPowerMultiplier * this->cyclingRatio;
     660              :     }
     661           21 : }
     662              : 
     663            8 : void EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP(EnergyPlusData &state)
     664              : {
     665              : 
     666              :     // energy balance on heat pump
     667            8 :     this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
     668              : 
     669              :     // calculate source side outlet conditions
     670            8 :     Real64 const CpSrc = this->sourceSidePlantLoc.loop->glycol->getSpecificHeat(
     671              :         state, this->sourceSideInletTemp, "EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP()");
     672            8 :     Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
     673            8 :     this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
     674              : 
     675            8 :     if (this->waterSource && abs(this->sourceSideOutletTemp - this->sourceSideInletTemp) > 100.0) { // whoaa out of range happenings on water loop
     676              :         //
     677              :         // TODO setup recurring error warning?
     678              :         // lets do something different than fatal the simulation
     679            0 :         if ((this->sourceSideMassFlowRate / this->sourceSideDesignMassFlowRate) < 0.01) { // current source side flow is 1% of design max
     680              :             // just send it all to skin losses and leave the fluid temperature alone
     681            0 :             this->sourceSideOutletTemp = this->sourceSideInletTemp;
     682            0 :         } else if (this->sourceSideOutletTemp > this->sourceSideInletTemp) {
     683            0 :             this->sourceSideOutletTemp = this->sourceSideInletTemp + 100.0; // cap it at 100C delta
     684              : 
     685            0 :         } else if (this->sourceSideOutletTemp < this->sourceSideInletTemp) {
     686            0 :             this->sourceSideOutletTemp = this->sourceSideInletTemp - 100.0; // cap it at 100C delta
     687              :         }
     688              :     }
     689            8 : }
     690              : 
     691            7 : void EIRPlantLoopHeatPump::calcSourceSideHeatTransferASHP(EnergyPlusData &state)
     692              : {
     693              :     // energy balance on heat pump
     694            7 :     this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
     695              : 
     696              :     // calculate source side outlet conditions
     697            7 :     Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
     698            7 :     Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
     699            7 :     this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
     700            7 :     if (this->heatRecoveryAvailable && !this->heatRecoveryIsActive) {
     701              :         // reset the HR report variables
     702            0 :         this->heatRecoveryRate = 0.0;
     703            0 :         this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
     704              :     }
     705            7 : }
     706              : 
     707            4 : void EIRPlantLoopHeatPump::calcHeatRecoveryHeatTransferASHP(EnergyPlusData &state)
     708              : {
     709              :     // energy balance on heat pump
     710            4 :     this->heatRecoveryRate = this->calcQheatRecovery(this->loadSideHeatTransfer, this->powerUsage);
     711            4 :     Real64 heatRecoverRateTot = this->heatRecoveryRate;
     712              : 
     713              :     // calculate heat recovery side outlet conditions
     714            4 :     Real64 const CpHR = this->heatRecoveryPlantLoc.loop->glycol->getSpecificHeat(
     715              :         state, this->heatRecoveryInletTemp, "EIRPlantLoopHeatPump::calcHeatRecoveryHeatTransferASHP()");
     716            4 :     Real64 const hRecoveryMCp = this->heatRecoveryMassFlowRate * CpHR;
     717            4 :     if (this->heatRecoveryMassFlowRate > 0.0) {
     718            4 :         this->heatRecoveryOutletTemp = this->calcHROutletTemp(this->heatRecoveryInletTemp, this->heatRecoveryRate / hRecoveryMCp);
     719              :     } else {
     720            0 :         this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
     721            0 :         this->heatRecoveryRate = 0.0;
     722              :     }
     723              :     // limit the HR HW outlet temperature to the maximum allowed (HW Recovery)
     724            4 :     if (this->heatRecoveryOutletTemp > this->maxHeatRecoveryTempLimit) {
     725            1 :         if (this->heatRecoveryInletTemp < this->maxHeatRecoveryTempLimit) {
     726            1 :             this->heatRecoveryOutletTemp = this->maxHeatRecoveryTempLimit;
     727            1 :             this->heatRecoveryRate = hRecoveryMCp * (this->heatRecoveryOutletTemp - this->heatRecoveryInletTemp);
     728              :         } else {
     729            0 :             this->heatRecoveryRate = 0.0;
     730            0 :             this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
     731              :         }
     732              :     }
     733              :     // limit the HR CW outlet temp to the minimum allowed (CW Recovery)
     734            4 :     if (this->heatRecoveryOutletTemp < this->minHeatRecoveryTempLimit) {
     735            1 :         if (this->heatRecoveryInletTemp > this->minHeatRecoveryTempLimit) {
     736            1 :             this->heatRecoveryOutletTemp = this->minHeatRecoveryTempLimit;
     737            1 :             this->heatRecoveryRate = hRecoveryMCp * (this->heatRecoveryInletTemp - this->heatRecoveryOutletTemp);
     738              :         } else {
     739            0 :             this->heatRecoveryRate = 0.0;
     740            0 :             this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
     741              :         }
     742              :     }
     743              :     // report the net heat balance as source side heat transfer
     744            4 :     Real64 heatReoveryRateUnused = std::max(0.0, (heatRecoverRateTot - this->heatRecoveryRate));
     745            4 :     if (heatReoveryRateUnused > 0.0) {
     746            2 :         this->sourceSideHeatTransfer = heatReoveryRateUnused;
     747              :         // calculate source side outlet conditions
     748            2 :         Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
     749            2 :         Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
     750            2 :         this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
     751              :     } else {
     752              :         // reset the source side report variables
     753            2 :         this->sourceSideHeatTransfer = 0.0;
     754            2 :         this->sourceSideOutletTemp = this->sourceSideInletTemp;
     755              :     }
     756            4 : }
     757              : 
     758           34 : void EIRPlantLoopHeatPump::setHeatRecoveryOperatingStatusASHP([[maybe_unused]] EnergyPlusData &state, [[maybe_unused]] bool FirstHVACIteration)
     759              : {
     760           34 :     if (!this->running) {
     761            5 :         if (this->heatRecoveryAvailable) {
     762              :             // set the HR operation off
     763            2 :             this->heatRecoveryIsActive = false;
     764            2 :             this->heatRecoveryOperatingStatus = 0;
     765              :         }
     766              :     } else { // the heat pump must be running
     767           29 :         if (this->heatRecoveryAvailable) {
     768              :             // apply min/max HR operating limits based on heat recovery entering fluid temperature
     769            4 :             if (this->minHeatRecoveryTempLimit > this->heatRecoveryInletTemp || this->maxHeatRecoveryTempLimit < this->heatRecoveryInletTemp) {
     770              :                 // set the HR operation off
     771            0 :                 this->heatRecoveryIsActive = false;
     772            0 :                 this->heatRecoveryOperatingStatus = 0;
     773              :             } else {
     774              :                 // set the HR operation on
     775            4 :                 this->heatRecoveryIsActive = true;
     776            4 :                 this->heatRecoveryOperatingStatus = 1;
     777              :             }
     778              :         }
     779              :     }
     780           34 : }
     781              : 
     782           19 : void EIRPlantLoopHeatPump::capModFTCurveCheck(EnergyPlusData &state, const Real64 loadSideOutletSetpointTemp, Real64 &capacityModifierFuncTemp)
     783              : {
     784           19 :     if (capacityModifierFuncTemp < 0.0) {
     785            0 :         if (this->capModFTErrorIndex == 0) {
     786            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     787            0 :             ShowContinueError(state,
     788            0 :                               format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
     789            0 :             ShowContinueError(state,
     790            0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
     791              :                                      loadSideOutletSetpointTemp,
     792            0 :                                      this->sourceSideInletTemp));
     793            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     794              :         }
     795            0 :         ShowRecurringWarningErrorAtEnd(state,
     796            0 :                                        format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
     797            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     798            0 :                                               this->name),
     799            0 :                                        this->capModFTErrorIndex,
     800              :                                        capacityModifierFuncTemp,
     801              :                                        capacityModifierFuncTemp);
     802            0 :         capacityModifierFuncTemp = 0.0;
     803              :     }
     804           19 : }
     805              : 
     806            0 : void EIRPlantLoopHeatPump::heatRecoveryCapModFTCurveCheck(EnergyPlusData &state,
     807              :                                                           const Real64 loadSideOutletSetpointTemp,
     808              :                                                           Real64 &capacityModifierFuncTemp)
     809              : {
     810            0 :     if (capacityModifierFuncTemp < 0.0) {
     811            0 :         if (this->heatRecCapModFTErrorIndex == 0) {
     812            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     813            0 :             ShowContinueError(state,
     814            0 :                               format(" Heat Recovery mode Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).",
     815              :                                      capacityModifierFuncTemp));
     816            0 :             ShowContinueError(
     817              :                 state,
     818            0 :                 format(
     819              :                     " Negative value occurs using a load side water temperature of {:.2T}C and heat recovery entering water temperature of {:.2T}C.",
     820              :                     loadSideOutletSetpointTemp,
     821            0 :                     this->heatRecoveryInletTemp));
     822            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     823              :         }
     824            0 :         ShowRecurringWarningErrorAtEnd(
     825              :             state,
     826            0 :             format("{} \"{}\": Heat Recovery mode Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
     827            0 :                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     828            0 :                    this->name),
     829            0 :             this->heatRecCapModFTErrorIndex,
     830              :             capacityModifierFuncTemp,
     831              :             capacityModifierFuncTemp);
     832            0 :         capacityModifierFuncTemp = 0.0;
     833              :     }
     834            0 : }
     835              : 
     836           21 : void EIRPlantLoopHeatPump::eirModCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncTemp)
     837              : {
     838           21 :     if (eirModifierFuncTemp < 0.0) {
     839            0 :         if (this->eirModFTErrorIndex == 0) {
     840            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     841            0 :             ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
     842            0 :             ShowContinueError(state,
     843            0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
     844            0 :                                      this->loadSideOutletTemp,
     845            0 :                                      this->sourceSideInletTemp));
     846            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     847              :         }
     848            0 :         ShowRecurringWarningErrorAtEnd(state,
     849            0 :                                        format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
     850            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     851            0 :                                               this->name),
     852            0 :                                        this->eirModFTErrorIndex,
     853              :                                        eirModifierFuncTemp,
     854              :                                        eirModifierFuncTemp);
     855            0 :         eirModifierFuncTemp = 0.0;
     856              :     }
     857           21 : }
     858              : 
     859            0 : void EIRPlantLoopHeatPump::heatRecoveryEIRModCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncTemp)
     860              : {
     861            0 :     if (eirModifierFuncTemp < 0.0) {
     862            0 :         if (this->heatRecEIRModFTErrorIndex == 0 && heatRecoveryEIRFTempCurveIndex > 0) {
     863            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     864            0 :             ShowContinueError(
     865            0 :                 state, format(" Heat Recovery mode EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
     866            0 :             ShowContinueError(
     867              :                 state,
     868            0 :                 format(
     869              :                     " Negative value occurs using a load side water temperature of {:.2T}C and heat recovery entering water temperature of {:.2T}C.",
     870            0 :                     this->loadSideOutletTemp,
     871            0 :                     this->heatRecoveryInletTemp));
     872            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     873              :         }
     874            0 :         ShowRecurringWarningErrorAtEnd(
     875              :             state,
     876            0 :             format("{} \"{}\": Heat Recovery mode EIR Modifier curve (function of Temperatures) output is negative warning continues...",
     877            0 :                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     878            0 :                    this->name),
     879            0 :             this->eirModFTErrorIndex,
     880              :             eirModifierFuncTemp,
     881              :             eirModifierFuncTemp);
     882            0 :         eirModifierFuncTemp = 0.0;
     883              :     }
     884            0 : }
     885              : 
     886           21 : void EIRPlantLoopHeatPump::eirModFPLRCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncPLR)
     887              : {
     888           21 :     if (eirModifierFuncPLR < 0.0) {
     889            1 :         if (this->eirModFPLRErrorIndex == 0) {
     890            1 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     891            1 :             ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
     892            1 :             ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", this->partLoadRatio));
     893            3 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     894              :         }
     895            7 :         ShowRecurringWarningErrorAtEnd(state,
     896            2 :                                        format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
     897            1 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     898            1 :                                               this->name),
     899            1 :                                        this->eirModFPLRErrorIndex,
     900              :                                        eirModifierFuncPLR,
     901              :                                        eirModifierFuncPLR);
     902            1 :         eirModifierFuncPLR = 0.0;
     903              :     }
     904           21 : }
     905              : 
     906           11 : void EIRPlantLoopHeatPump::doDefrost(EnergyPlusData &state, Real64 &availableCapacity)
     907              : {
     908              :     // Initializing defrost adjustment factors
     909           11 :     Real64 InputPowerMultiplier = 1.0;
     910           11 :     Real64 HeatingCapacityMultiplier = 1.0;
     911              : 
     912              :     // Check outdoor temperature to determine of defrost is active
     913           11 :     if (this->defrostAvailable && state.dataEnvrn->OutDryBulbTemp <= this->maxOutdoorTemperatureDefrost) {
     914              :         // Calculate defrost adjustment factors depending on defrost control type
     915              :         // Calculate delta w through outdoor coil by assuming a coil temp of 0.82*DBT-9.7(F) per DOE2.1E
     916            0 :         Real64 OutdoorCoilT = 0.82 * state.dataEnvrn->OutDryBulbTemp - 8.589;
     917              :         Real64 OutdoorCoildw =
     918            0 :             max(1.0e-6, (state.dataEnvrn->OutHumRat - Psychrometrics::PsyWFnTdpPb(state, OutdoorCoilT, state.dataEnvrn->OutBaroPress)));
     919            0 :         if (this->defrostStrategy == DefrostControl::Timed) {
     920            0 :             if (this->defrostTime > 0.0) {
     921            0 :                 this->fractionalDefrostTime = this->defrostTime; // DefrostTime in hours
     922            0 :                 HeatingCapacityMultiplier = 0.909 - 107.33 * OutdoorCoildw;
     923            0 :                 InputPowerMultiplier = 0.90 - 36.45 * OutdoorCoildw;
     924            0 :                 this->loadDueToDefrost =
     925            0 :                     (0.01 * this->fractionalDefrostTime) * (7.222 - state.dataEnvrn->OutDryBulbTemp) * (this->referenceCapacity / 1.01667);
     926            0 :                 Real64 defrostEIRFT = 1.0 / this->referenceCOP;
     927            0 :                 if (defrostEIRFTIndex > 0) {
     928            0 :                     defrostEIRFT = Curve::CurveValue(
     929            0 :                         state, this->defrostEIRFTIndex, max(15.555, state.dataEnvrn->OutWetBulbTemp), max(15.555, state.dataEnvrn->OutDryBulbTemp));
     930              :                 }
     931            0 :                 this->defrostEnergyRate = defrostEIRFT * (this->referenceCapacity / 1.01667) * this->fractionalDefrostTime;
     932              :             } else {
     933            0 :                 this->loadDueToDefrost = 0.0;
     934            0 :                 this->defrostEnergyRate = 0.0;
     935            0 :                 this->fractionalDefrostTime = 0.0;
     936              :             }
     937            0 :         } else if (this->defrostStrategy == DefrostControl::OnDemand) {
     938            0 :             this->fractionalDefrostTime = 1.0 / (1.0 + 0.01446 / OutdoorCoildw);
     939            0 :             HeatingCapacityMultiplier = 0.875 * (1.0 - this->fractionalDefrostTime);
     940            0 :             InputPowerMultiplier = 0.954 * (1.0 - this->fractionalDefrostTime);
     941            0 :             this->loadDueToDefrost =
     942            0 :                 (0.01 * this->fractionalDefrostTime) * (7.222 - state.dataEnvrn->OutDryBulbTemp) * (this->referenceCapacity / 1.01667);
     943            0 :             Real64 defrostEIRFT = 0.0;
     944            0 :             if (defrostEIRFTIndex > 0) {
     945            0 :                 defrostEIRFT = Curve::CurveValue(
     946            0 :                     state, this->defrostEIRFTIndex, max(15.555, state.dataEnvrn->OutWetBulbTemp), max(15.555, state.dataEnvrn->OutDryBulbTemp));
     947              :             }
     948            0 :             this->defrostEnergyRate = defrostEIRFT * (this->referenceCapacity / 1.01667) * this->fractionalDefrostTime;
     949            0 :         } else if (this->defrostStrategy == DefrostControl::TimedEmpirical) {
     950              :             // cycles of defrost per hour
     951            0 :             Real64 thisHourDefrostCycles = Curve::CurveValue(state, this->defrostFreqCurveIndex, state.dataEnvrn->OutDryBulbTemp);
     952              :             // is directly proportional to the ratio of capacity used for that hour (PLR)
     953            0 :             Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio;
     954            0 :             thisHourDefrostCycles *= operatingPLR;
     955              :             // fraction of heat load per cycle of defrost
     956            0 :             Real64 thisHourDefrostHeatLoad = 0.0;
     957            0 :             if (this->defrostLoadCurveDims == 2) { // BiQuadratic
     958              :                 thisHourDefrostHeatLoad =
     959            0 :                     Curve::CurveValue(state, this->defrostHeatLoadCurveIndex, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutDryBulbTemp);
     960              :             } else {
     961            0 :                 thisHourDefrostHeatLoad = Curve::CurveValue(state, this->defrostHeatLoadCurveIndex, state.dataEnvrn->OutDryBulbTemp);
     962              :             }
     963              :             // heat load is applied to full load (not rated) and is proportional to defrost cycles per hour
     964            0 :             this->loadDueToDefrost = availableCapacity * thisHourDefrostHeatLoad * thisHourDefrostCycles;
     965              :             // electric input fraction due to defrost
     966            0 :             Real64 defrostHeatEnergyFraction = 0.0;
     967            0 :             if (this->defrostEnergyCurveDims == 2) { // BiQuadratic
     968              :                 defrostHeatEnergyFraction =
     969            0 :                     Curve::CurveValue(state, this->defrostHeatEnergyCurveIndex, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutDryBulbTemp);
     970              :             } else {
     971            0 :                 defrostHeatEnergyFraction = Curve::CurveValue(state, this->defrostHeatEnergyCurveIndex, state.dataEnvrn->OutDryBulbTemp);
     972              :             }
     973              :             // defrost energy rate is applied to rated power and is proportional to defrost cycles per hour
     974            0 :             this->defrostEnergyRate = (this->referenceCapacity / this->referenceCOP) * defrostHeatEnergyFraction * thisHourDefrostCycles;
     975              : 
     976              :             // question on how these multipliers are accounted for with capacity and power (e.g., 1+ or 1-)
     977            0 :             InputPowerMultiplier = 1.0 + thisHourDefrostHeatLoad;
     978            0 :             HeatingCapacityMultiplier = 1.0 + (thisHourDefrostHeatLoad * thisHourDefrostCycles);
     979            0 :             this->fractionalDefrostTime = thisHourDefrostCycles * this->fractionalDefrostTime;
     980              :         }
     981              :     } else {
     982           11 :         this->defrostEnergyRate = 0.0;
     983           11 :         this->loadDueToDefrost = 0.0;
     984           11 :         this->fractionalDefrostTime = 0.0;
     985              :     }
     986           11 :     availableCapacity *= HeatingCapacityMultiplier;
     987              :     // update class member variables
     988           11 :     this->defrostPowerMultiplier = InputPowerMultiplier;
     989           11 : }
     990              : 
     991           41 : void EIRPlantLoopHeatPump::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
     992              : {
     993              :     // This function does all one-time and begin-environment initialization
     994           95 :     std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
     995              : 
     996           41 :     this->oneTimeInit(state);          // plant setup
     997           36 :     this->isPlantInletOrOutlet(state); // check location
     998              : 
     999           36 :     if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
    1000           36 :         this->sizeLoadSide(state);
    1001           36 :         if (this->waterSource) {
    1002           15 :             this->sizeSrcSideWSHP(state);
    1003           21 :         } else if (this->airSource) {
    1004           21 :             this->sizeSrcSideASHP(state);
    1005           21 :             this->sizeHeatRecoveryASHP(state);
    1006              :         }
    1007              :     }
    1008              : 
    1009           36 :     if (state.dataGlobal->BeginEnvrnFlag && this->envrnInit && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
    1010              : 
    1011           23 :         Real64 rho = this->loadSidePlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
    1012           23 :         this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
    1013           23 :         PlantUtilities::InitComponentNodes(state, 0.0, this->loadSideDesignMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
    1014              : 
    1015           23 :         if (this->waterSource) {
    1016            7 :             rho = this->sourceSidePlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
    1017            7 :             this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
    1018            7 :             PlantUtilities::InitComponentNodes(
    1019              :                 state, 0.0, this->sourceSideDesignMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
    1020           16 :         } else if (this->airSource) {
    1021           16 :             rho = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, state.dataEnvrn->OutDryBulbTemp, 0.0, routineName);
    1022           16 :             this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
    1023              :             // heat recovery
    1024           16 :             if (this->heatRecoveryAvailable) {
    1025            6 :                 rho = this->heatRecoveryPlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
    1026            6 :                 this->heatRecoveryDesignMassFlowRate = rho * this->heatRecoveryDesignVolFlowRate;
    1027            6 :                 PlantUtilities::InitComponentNodes(
    1028              :                     state, 0.0, this->heatRecoveryDesignMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet);
    1029              :             }
    1030              :         }
    1031              : 
    1032           23 :         if (this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
    1033            0 :             this->loadVSPumpMinLimitMassFlow =
    1034            0 :                 PlantUtilities::MinFlowIfBranchHasVSPump(state, this->loadSidePlantLoc, this->loadVSBranchPump, this->loadVSLoopPump, true);
    1035            0 :             if (this->waterSource) {
    1036            0 :                 this->sourceVSPumpMinLimitMassFlow = PlantUtilities::MinFlowIfBranchHasVSPump(
    1037            0 :                     state, this->sourceSidePlantLoc, this->sourceVSBranchPump, this->sourceVSLoopPump, false);
    1038              :             }
    1039              :         }
    1040              : 
    1041           23 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1042            8 :             this->envrnInit = false;
    1043              :         }
    1044              :     }
    1045           36 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    1046            1 :         this->envrnInit = true;
    1047              :     }
    1048           36 : }
    1049              : 
    1050            4 : void EIRPlantLoopHeatPump::getDesignCapacities(
    1051              :     [[maybe_unused]] EnergyPlusData &state, const PlantLocation &calledFromLocation, Real64 &MaxLoad, Real64 &MinLoad, Real64 &OptLoad)
    1052              : {
    1053            4 :     if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
    1054            3 :         MinLoad = 0.0;
    1055            3 :         MaxLoad = this->referenceCapacity;
    1056            3 :         OptLoad = this->referenceCapacity;
    1057              :     } else {
    1058            1 :         MinLoad = 0.0;
    1059            1 :         MaxLoad = 0.0;
    1060            1 :         OptLoad = 0.0;
    1061              :     }
    1062            4 : }
    1063              : 
    1064           49 : void EIRPlantLoopHeatPump::sizeLoadSide(EnergyPlusData &state)
    1065              : {
    1066              :     // Tries to size the load side flow rate and capacity, source side flow, and the rated power usage
    1067              :     // There are two major sections to this function, one if plant sizing is available, and one if not
    1068              :     // If plant sizing is available, then we can generate sizes for the equipment.  This is done for not-only
    1069              :     //   autosized fields, but also hard-sized fields so that we can report out significant deviations between
    1070              :     //   the two values.
    1071              :     // If plant sizing is not available, it tries to use a companion heat pump coil to do sizing
    1072              : 
    1073           49 :     bool errorsFound = false;
    1074              : 
    1075              :     // these variables will be used throughout this function as a temporary value of that physical state
    1076           49 :     Real64 tmpCapacity = this->referenceCapacity;
    1077           49 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    1078           49 :     HeatSizingType heatingSizingMethod = this->heatSizingMethod;
    1079              : 
    1080           49 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
    1081           49 :     Real64 loadSideInitTemp =
    1082           49 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
    1083              :     // I guess I can assume the plant fluids are the same for HW and CW. So only the sizing type is an issue on which to use.
    1084              : 
    1085           49 :     Real64 rho = this->loadSidePlantLoc.loop->glycol->getDensity(state, loadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
    1086           49 :     Real64 Cp = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, loadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
    1087              : 
    1088           49 :     int pltLoadSizNum = this->loadSidePlantLoc.loop->PlantSizNum;
    1089           49 :     if (pltLoadSizNum > 0) {
    1090              :         // this first IF block is really just about calculating the local tmpCapacity and tmpLoadVolFlow values
    1091              :         // these represent what the unit would size those to, whether it is doing auto-sizing or not
    1092           19 :         if (state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate > HVAC::SmallWaterVolFlow) {
    1093            7 :             tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate * this->sizingFactor;
    1094            7 :             Real64 deltaT = state.dataSize->PlantSizData(pltLoadSizNum).DeltaT;
    1095            7 :             if (this->companionHeatPumpCoil) {
    1096            6 :                 if (this->companionHeatPumpCoil->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1097            3 :                     heatingSizingMethod = this->companionHeatPumpCoil->heatSizingMethod;
    1098              :                 }
    1099            6 :                 Real64 companionVolFlowRate = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
    1100            6 :                 int compLoopNum = this->companionHeatPumpCoil->loadSidePlantLoc.loopNum;
    1101            6 :                 if (compLoopNum > 0) {
    1102            3 :                     companionVolFlowRate = state.dataSize->PlantSizData(compLoopNum).DesVolFlowRate * this->companionHeatPumpCoil->sizingFactor;
    1103              :                 }
    1104            6 :                 Real64 compRefCapacity = this->companionHeatPumpCoil->referenceCapacity;
    1105            6 :                 Real64 compRho = rho;
    1106            6 :                 Real64 compCp = Cp;
    1107            6 :                 Real64 compDeltaT = deltaT;
    1108            6 :                 if (compLoopNum > 0) {
    1109            3 :                     compRho = state.dataPlnt->PlantLoop(compLoopNum)
    1110            6 :                                   .glycol->getDensity(state,
    1111            3 :                                                       this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling ? Constant::HWInitConvTemp
    1112              :                                                                                                                            : Constant::CWInitConvTemp,
    1113              :                                                       "EIRPlantLoopHeatPump::sizeLoadSide()");
    1114              :                     compCp =
    1115            3 :                         state.dataPlnt->PlantLoop(compLoopNum)
    1116            6 :                             .glycol->getSpecificHeat(state,
    1117            3 :                                                      this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling ? Constant::HWInitConvTemp
    1118              :                                                                                                                           : Constant::CWInitConvTemp,
    1119              :                                                      "EIRPlantLoopHeatPump::sizeLoadSide()");
    1120            3 :                     compDeltaT = state.dataSize->PlantSizData(compLoopNum).DeltaT;
    1121              :                 }
    1122            6 :                 if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
    1123            3 :                     tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
    1124            3 :                     if (heatingSizingMethod == HeatSizingType::Heating) {
    1125            0 :                         tmpCapacity = (compCp * compRho * compDeltaT * companionVolFlowRate) / this->companionHeatPumpCoil->heatSizingRatio;
    1126            3 :                     } else if (heatingSizingMethod == HeatSizingType::GreaterOfCoolingOrHeating) {
    1127            0 :                         compRefCapacity = compCp * compRho * compDeltaT * companionVolFlowRate;
    1128            0 :                         if (compRefCapacity > tmpCapacity) {
    1129            0 :                             rho = compRho;
    1130            0 :                             tmpLoadVolFlow = companionVolFlowRate;
    1131            0 :                             tmpCapacity = compRefCapacity / this->companionHeatPumpCoil->heatSizingRatio;
    1132              :                         }
    1133              :                     }
    1134              :                 } else { // size heating side based on sizing method
    1135            3 :                     if (heatingSizingMethod == HeatSizingType::Heating) {
    1136            0 :                         tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
    1137              :                     } else {
    1138            3 :                         compRefCapacity = compCp * compRho * compDeltaT * companionVolFlowRate;
    1139            3 :                         if (heatingSizingMethod == HeatSizingType::Cooling) {
    1140            3 :                             tmpCapacity = compRefCapacity * this->heatSizingRatio;
    1141            3 :                             rho = compRho;
    1142            3 :                             tmpLoadVolFlow = companionVolFlowRate;
    1143              :                         } else { // else GreaterOfHeatingOrCooling
    1144            0 :                             tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
    1145            0 :                             if (compRefCapacity > tmpCapacity) {
    1146            0 :                                 tmpCapacity = compRefCapacity * this->heatSizingRatio;
    1147            0 :                                 rho = compRho;
    1148            0 :                                 tmpLoadVolFlow = companionVolFlowRate;
    1149              :                             }
    1150              :                         }
    1151              :                     }
    1152              :                 }
    1153              :             } else {
    1154            1 :                 tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow * this->heatSizingRatio;
    1155              :             }
    1156           12 :         } else if (this->companionHeatPumpCoil && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
    1157           10 :             tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
    1158           10 :             if (this->companionHeatPumpCoil->referenceCapacity == DataSizing::AutoSize) {
    1159              :                 // use reverse init temp, e.g., if this is cooling use HWInitConvTemp
    1160            4 :                 Real64 compLoadSideInitTemp =
    1161            4 :                     (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
    1162            4 :                 int compLoopNum = this->companionHeatPumpCoil->loadSidePlantLoc.loopNum;
    1163            4 :                 if (compLoopNum > 0) {
    1164            0 :                     Real64 const compRho = state.dataPlnt->PlantLoop(compLoopNum)
    1165            0 :                                                .glycol->getDensity(state, compLoadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
    1166            0 :                     Real64 const compCp = state.dataPlnt->PlantLoop(compLoopNum)
    1167            0 :                                               .glycol->getSpecificHeat(state, Constant::CWInitConvTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
    1168            0 :                     rho = compRho;
    1169            0 :                     Cp = compCp;
    1170              :                 }
    1171            4 :                 tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow * this->heatSizingRatio;
    1172              :             } else {
    1173            6 :                 tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
    1174              :             }
    1175           10 :         } else {
    1176            2 :             if (this->referenceCapacityWasAutoSized) {
    1177            2 :                 tmpCapacity = 0.0;
    1178              :             }
    1179            2 :             if (this->loadSideDesignVolFlowRateWasAutoSized) {
    1180            2 :                 tmpLoadVolFlow = 0.0;
    1181              :             }
    1182              :         }
    1183           19 :         if (this->heatRecoveryHeatPump) {
    1184            0 :             tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate;
    1185              :         }
    1186           19 :         if (this->loadSideDesignVolFlowRateWasAutoSized) {
    1187            8 :             this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
    1188              :         }
    1189           19 :         if (this->referenceCapacityWasAutoSized) {
    1190           16 :             this->referenceCapacity = tmpCapacity;
    1191              :         }
    1192              :         // now we actually need to store and report out the values
    1193           19 :         if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
    1194              :             // handle the auto-sizable reference capacity
    1195           18 :             if (this->referenceCapacityWasAutoSized) {
    1196              :                 // if auto-sized, we just need to store the sized value and then report out the capacity when plant is ready
    1197           16 :                 this->referenceCapacity = tmpCapacity;
    1198           16 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1199           16 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
    1200              :                 }
    1201           16 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1202           16 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
    1203              :                 }
    1204              :             } else {
    1205              :                 // this blocks means the capacity value was hard-sized
    1206            2 :                 if (this->referenceCapacity > 0.0 && tmpCapacity > 0.0) {
    1207              :                     // then the capacity was hard-sized to a good value and the tmpCapacity was calculated to a good value too
    1208            2 :                     Real64 hardSizedCapacity = this->referenceCapacity;
    1209            2 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1210            2 :                         if (state.dataGlobal->DoPlantSizing) {
    1211            1 :                             BaseSizer::reportSizerOutput(state,
    1212              :                                                          typeName,
    1213              :                                                          this->name,
    1214              :                                                          "Design Size Nominal Capacity [W]",
    1215              :                                                          tmpCapacity,
    1216              :                                                          "User-Specified Nominal Capacity [W]",
    1217              :                                                          hardSizedCapacity);
    1218              :                         } else {
    1219            1 :                             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", hardSizedCapacity);
    1220              :                         }
    1221              :                         // we can warn here if there is a bit mismatch between hard- and auto-sized
    1222            2 :                         if (state.dataGlobal->DisplayExtraWarnings) {
    1223            2 :                             if ((std::abs(tmpCapacity - hardSizedCapacity) / hardSizedCapacity) > state.dataSize->AutoVsHardSizingThreshold) {
    1224            4 :                                 ShowWarningMessage(state,
    1225            4 :                                                    format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
    1226            2 :                                 ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", hardSizedCapacity));
    1227            2 :                                 ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpCapacity));
    1228            4 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1229            6 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1230              :                             }
    1231              :                         }
    1232              :                     }
    1233              :                     // moving forward with more calculations, we need to update the 'tmp' capacity to the hard-sized value
    1234            2 :                     tmpCapacity = hardSizedCapacity;
    1235              :                 }
    1236              :             }
    1237              :             // now handle the auto-sizable load side flow rate
    1238           18 :             if (this->loadSideDesignVolFlowRateWasAutoSized) {
    1239            8 :                 this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
    1240            8 :                 this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
    1241            8 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1242            8 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
    1243              :                 }
    1244            8 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1245            8 :                     BaseSizer::reportSizerOutput(
    1246              :                         state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
    1247              :                 }
    1248              :             } else {
    1249           10 :                 if (this->loadSideDesignVolFlowRate > 0.0 && tmpLoadVolFlow > 0.0) {
    1250           10 :                     Real64 hardSizedLoadSideFlow = this->loadSideDesignVolFlowRate;
    1251           10 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1252           10 :                         if (state.dataGlobal->DoPlantSizing) {
    1253            1 :                             BaseSizer::reportSizerOutput(state,
    1254              :                                                          typeName,
    1255              :                                                          this->name,
    1256              :                                                          "Design Size Load Side Volume Flow Rate [m3/s]",
    1257              :                                                          tmpLoadVolFlow,
    1258              :                                                          "User-Specified Load Side Volume Flow Rate [m3/s]",
    1259              :                                                          hardSizedLoadSideFlow);
    1260              :                         } else {
    1261            9 :                             BaseSizer::reportSizerOutput(
    1262              :                                 state, typeName, this->name, "User-Specified Load Side Volume Flow Rate [m3/s]", hardSizedLoadSideFlow);
    1263              :                         }
    1264           10 :                         if (state.dataGlobal->DisplayExtraWarnings) {
    1265            2 :                             if ((std::abs(tmpLoadVolFlow - hardSizedLoadSideFlow) / hardSizedLoadSideFlow) >
    1266            2 :                                 state.dataSize->AutoVsHardSizingThreshold) {
    1267            0 :                                 ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
    1268            0 :                                 ShowContinueError(state, format("User-Specified Load Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedLoadSideFlow));
    1269            0 :                                 ShowContinueError(state,
    1270            0 :                                                   format("differs from Design Size Load Side Volume Flow Rate of {:.2R} [m3/s]", tmpLoadVolFlow));
    1271            0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1272            0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1273              :                             }
    1274              :                         }
    1275              :                     }
    1276           10 :                     tmpLoadVolFlow = hardSizedLoadSideFlow;
    1277              :                 }
    1278              :             }
    1279              :         }
    1280              :     } else {
    1281              :         // no plant sizing available...try to use the companion coil
    1282           30 :         if (this->companionHeatPumpCoil) {
    1283           11 :             if (this->companionHeatPumpCoil->loadSideDesignVolFlowRateWasAutoSized && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
    1284            1 :                 tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
    1285            1 :                 if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
    1286            1 :                     this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
    1287            1 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1288            1 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
    1289              :                     }
    1290            1 :                     if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1291            1 :                         BaseSizer::reportSizerOutput(
    1292              :                             state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
    1293              :                     }
    1294              :                 }
    1295              :             }
    1296           11 :             if (this->companionHeatPumpCoil->referenceCapacityWasAutoSized && this->companionHeatPumpCoil->referenceCapacity > 0.0) {
    1297            1 :                 tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
    1298            1 :                 if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
    1299            1 :                     this->referenceCapacity = tmpCapacity;
    1300            1 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1301            1 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
    1302              :                     }
    1303            1 :                     if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1304            1 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
    1305              :                     }
    1306              :                 }
    1307              :             }
    1308              :         } else {
    1309              :             // no companion coil, and no plant sizing, so can't do anything
    1310           22 :             if ((this->loadSideDesignVolFlowRateWasAutoSized || this->referenceCapacityWasAutoSized) &&
    1311            3 :                 state.dataPlnt->PlantFirstSizesOkayToFinalize) {
    1312            2 :                 ShowSevereError(state, "EIRPlantLoopHeatPump::size(): Autosizing requires a loop Sizing:Plant object.");
    1313            1 :                 ShowContinueError(state, format("Occurs in HeatPump:PlantLoop:EquationFit:Cooling object = {}", this->name));
    1314            1 :                 errorsFound = true;
    1315              :             }
    1316              :         }
    1317           30 :         if (!this->loadSideDesignVolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
    1318            1 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Load Side Flow Rate [m3/s]", this->loadSideDesignVolFlowRate);
    1319              :         }
    1320           30 :         if (!this->referenceCapacityWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
    1321            1 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", this->referenceCapacity);
    1322              :         }
    1323              :     }
    1324           49 :     if (errorsFound) {
    1325            3 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
    1326              :     }
    1327           48 : }
    1328              : 
    1329           22 : void EIRPlantLoopHeatPump::sizeSrcSideWSHP(EnergyPlusData &state)
    1330              : {
    1331              :     // size the source-side for the water-source HP
    1332           22 :     bool errorsFound = false;
    1333              : 
    1334              :     // these variables will be used throughout this function as a temporary value of that physical state
    1335           22 :     Real64 tmpCapacity = this->referenceCapacity;
    1336           22 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    1337              :     Real64 tmpSourceVolFlow;
    1338              : 
    1339           22 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
    1340           22 :     Real64 sourceSideInitTemp =
    1341           22 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::CWInitConvTemp : Constant::HWInitConvTemp;
    1342              : 
    1343           22 :     Real64 const rhoSrc = this->loadSidePlantLoc.loop->glycol->getDensity(state, sourceSideInitTemp, "EIRPlantLoopHeatPump::sizeSrcSideWSHP()");
    1344           22 :     Real64 const CpSrc = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, sourceSideInitTemp, "EIRPlantLoopHeatPump::sizeSrcSideWSHP()");
    1345              : 
    1346              :     // To start we need to override the calculated load side flow
    1347              :     // rate if it was actually hard-sized
    1348           22 :     if (!this->loadSideDesignVolFlowRateWasAutoSized) {
    1349           13 :         tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    1350              :     }
    1351              : 
    1352              :     // calculate an auto-sized value for source design flow regardless of whether it was auto-sized or not
    1353           22 :     int plantSourceSizingIndex = this->sourceSidePlantLoc.loop->PlantSizNum;
    1354           22 :     if (plantSourceSizingIndex > 0) {
    1355              :         // to get the source flow, we first must calculate the required heat impact on the source side
    1356              :         // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
    1357              :         // Then the energy balance:     Qsrc = Qload + Power
    1358              :         // Substituting for Power:      Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
    1359            6 :         Real64 designSourceSideHeatTransfer = 0.0;
    1360            6 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1361            4 :             designSourceSideHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
    1362              :         } else {
    1363            2 :             designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
    1364              :         }
    1365              :         // To get the design source flow rate, just apply the sensible heat rate equation:
    1366              :         //                              Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
    1367              :         //                              Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
    1368            6 :         tmpSourceVolFlow = designSourceSideHeatTransfer / (state.dataSize->PlantSizData(plantSourceSizingIndex).DeltaT * CpSrc * rhoSrc);
    1369            6 :         if (this->waterSource && this->heatRecoveryHeatPump) {
    1370              :             // If component is on plant outlet branch, use plant flow rate.
    1371            0 :             tmpSourceVolFlow = state.dataSize->PlantSizData(plantSourceSizingIndex).DesVolFlowRate;
    1372              :         }
    1373              :     } else {
    1374              :         // just assume it's the same as the load side if we don't have any sizing information
    1375           16 :         tmpSourceVolFlow = tmpLoadVolFlow;
    1376              :     }
    1377           22 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
    1378            9 :         this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
    1379            9 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1380            4 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
    1381              :         }
    1382            9 :         if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1383            4 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
    1384              :         }
    1385              :     } else {
    1386              :         // source design flow was hard-sized
    1387           13 :         if (this->sourceSideDesignVolFlowRate > 0.0 && tmpSourceVolFlow > 0.0) {
    1388           10 :             Real64 const hardSizedSourceSideFlow = this->sourceSideDesignVolFlowRate;
    1389           10 :             if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1390            1 :                 if (state.dataGlobal->DoPlantSizing) {
    1391            0 :                     BaseSizer::reportSizerOutput(state,
    1392              :                                                  typeName,
    1393              :                                                  this->name,
    1394              :                                                  "Design Size Source Side Volume Flow Rate [m3/s]",
    1395              :                                                  tmpSourceVolFlow,
    1396              :                                                  "User-Specified Source Side Volume Flow Rate [m3/s]",
    1397              :                                                  hardSizedSourceSideFlow);
    1398              :                 } else {
    1399            1 :                     BaseSizer::reportSizerOutput(
    1400              :                         state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", hardSizedSourceSideFlow);
    1401              :                 }
    1402            1 :                 if (state.dataGlobal->DisplayExtraWarnings) {
    1403            0 :                     if ((std::abs(tmpSourceVolFlow - hardSizedSourceSideFlow) / hardSizedSourceSideFlow) >
    1404            0 :                         state.dataSize->AutoVsHardSizingThreshold) {
    1405            0 :                         ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
    1406            0 :                         ShowContinueError(state, format("User-Specified Source Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedSourceSideFlow));
    1407            0 :                         ShowContinueError(state, format("differs from Design Size Source Side Volume Flow Rate of {:.2R} [m3/s]", tmpSourceVolFlow));
    1408            0 :                         ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1409            0 :                         ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1410              :                     }
    1411              :                 }
    1412              :             }
    1413           10 :             tmpSourceVolFlow = hardSizedSourceSideFlow;
    1414              :         }
    1415              :     }
    1416           22 :     if (this->companionHeatPumpCoil) {
    1417           11 :         tmpSourceVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
    1418              :     } else {
    1419           11 :         tmpSourceVolFlow *= this->heatSizingRatio;
    1420              :     }
    1421              : 
    1422              :     // skipping autosized power section
    1423              : 
    1424              :     // register the design volume flows with the plant, only doing half of source because the companion
    1425              :     // is generally on the same loop
    1426           22 :     if (!this->heatRecoveryHeatPump) {
    1427           22 :         PlantUtilities::RegisterPlantCompDesignFlow(state, this->loadSideNodes.inlet, tmpLoadVolFlow);
    1428              :     }
    1429           22 :     if (!this->heatRecoveryHeatPump) {
    1430           22 :         PlantUtilities::RegisterPlantCompDesignFlow(state, this->sourceSideNodes.inlet, tmpSourceVolFlow / 0.5);
    1431              :     }
    1432              : 
    1433           22 :     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1434              :         // create predefined report
    1435            7 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, this->name, typeName);
    1436            7 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, this->name, this->referenceCOP);
    1437            7 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->name, this->referenceCapacity);
    1438              :     }
    1439              : 
    1440           22 :     if (errorsFound) {
    1441            0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
    1442              :     }
    1443           22 : }
    1444              : 
    1445           26 : void EIRPlantLoopHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
    1446              : {
    1447              :     // size the source-side for the air-source HP
    1448           26 :     bool errorsFound = false;
    1449              : 
    1450              :     // these variables will be used throughout this function as a temporary value of that physical state
    1451           26 :     Real64 tmpCapacity = this->referenceCapacity;
    1452           26 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    1453           26 :     Real64 tmpSourceVolFlow = 0.0;
    1454              : 
    1455              :     // will leave like this for now
    1456              :     // need to update these to better values later
    1457           26 :     Real64 sourceSideInitTemp = 20;
    1458           26 :     Real64 sourceSideHumRat = 0.0;
    1459           26 :     if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1460              :         // same here; update later
    1461            8 :         sourceSideInitTemp = 20;
    1462              :     }
    1463              : 
    1464           26 :     Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
    1465           26 :     Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
    1466              : 
    1467              :     // set the source-side flow rate
    1468           26 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
    1469              :         // load-side capacity should already be set, so unless the flow rate is specified, we can set
    1470              :         // an assumed reasonable flow rate since this doesn't affect downstream components
    1471           15 :         Real64 DeltaT_src = 10;
    1472              :         // to get the source flow, we first must calculate the required heat impact on the source side
    1473              :         // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
    1474              :         // Then the energy balance:     Qsrc = Qload + Power
    1475              :         // Substituting for Power:      Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
    1476           15 :         Real64 designSourceSideHeatTransfer = 0.0;
    1477           15 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1478            6 :             designSourceSideHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
    1479              :         } else {
    1480            9 :             designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
    1481              :         }
    1482              :         // To get the design source flow rate, just apply the sensible heat rate equation:
    1483              :         //                              Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
    1484              :         //                              Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
    1485           15 :         tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
    1486           11 :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0) {
    1487              :         // given the value by the user
    1488              :         // set it directly
    1489           11 :         tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
    1490              :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0) { // LCOV_EXCL_LINE
    1491              :         // user gave a flow rate of 0
    1492              :         // protected by the input processor to be >0.0
    1493              :         // fatal out just in case
    1494              :         errorsFound = true; // LCOV_EXCL_LINE
    1495            0 :         ShowSevereError(state,
    1496            0 :                         format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
    1497            0 :                                this->name,
    1498              :                                this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
    1499              :     } else {
    1500              :         // can't imagine how it would ever get to this point
    1501              :         // just assume it's the same as the load side if we don't have any sizing information
    1502              :         tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
    1503              :     }
    1504              : 
    1505           26 :     if (this->companionHeatPumpCoil) {
    1506           18 :         tmpSourceVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
    1507              :     } else {
    1508            8 :         tmpSourceVolFlow *= this->heatSizingRatio;
    1509              :     }
    1510           26 :     this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
    1511           26 :     this->sourceSideDesignMassFlowRate = rhoSrc * this->sourceSideDesignVolFlowRate;
    1512              : 
    1513           26 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
    1514           26 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
    1515           15 :         this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
    1516           15 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1517           12 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
    1518              :         }
    1519           15 :         if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1520           12 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
    1521              :         }
    1522              :     } else {
    1523              :         // source design flow was hard-sized
    1524           11 :         if (this->sourceSideDesignVolFlowRate > 0.0) {
    1525           11 :             if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1526            1 :                 if (state.dataGlobal->DoPlantSizing) {
    1527            0 :                     BaseSizer::reportSizerOutput(state,
    1528              :                                                  typeName,
    1529              :                                                  this->name,
    1530              :                                                  "Design Size Source Side Volume Flow Rate [m3/s]",
    1531              :                                                  tmpSourceVolFlow,
    1532              :                                                  "User-Specified Source Side Volume Flow Rate [m3/s]",
    1533            0 :                                                  this->sourceSideDesignVolFlowRate);
    1534              :                 } else {
    1535            1 :                     BaseSizer::reportSizerOutput(
    1536              :                         state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", this->sourceSideDesignVolFlowRate);
    1537              :                 }
    1538              :             }
    1539              :         }
    1540              :     }
    1541              : 
    1542           26 :     if (errorsFound) {
    1543              :         ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
    1544              :     }
    1545           26 : }
    1546              : 
    1547           21 : void EIRPlantLoopHeatPump::sizeHeatRecoveryASHP(EnergyPlusData &state)
    1548              : {
    1549              :     // size heat recovery side volume flow rate for air-source HP
    1550           21 :     if (!this->heatRecoveryAvailable) {
    1551           15 :         return;
    1552              :     }
    1553              : 
    1554              :     // these variables will be used throughout this function as a temporary value
    1555            6 :     Real64 tmpCapacity = this->referenceCapacity;
    1556            6 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    1557            6 :     Real64 tmpHeatRecoveryVolFlow = 0.0;
    1558              :     // size the heat-recovery flow rate
    1559            6 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
    1560            6 :     Real64 heatRecoveryInitTemp =
    1561            6 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
    1562              :     Real64 const rhoHR =
    1563            6 :         this->heatRecoveryPlantLoc.loop->glycol->getDensity(state, heatRecoveryInitTemp, "EIRPlantLoopHeatPump::sizeHeatRecoveryASHP()");
    1564              :     Real64 const CpHR =
    1565            6 :         this->heatRecoveryPlantLoc.loop->glycol->getSpecificHeat(state, heatRecoveryInitTemp, "EIRPlantLoopHeatPump::sizeHeatRecoveryASHP()");
    1566              : 
    1567              :     // calculate an auto-sized value for heat recovery design flow regardless of whether it was auto-sized or not
    1568            6 :     int plantHRSizingIndex = this->heatRecoveryPlantLoc.loop->PlantSizNum;
    1569            6 :     if (plantHRSizingIndex > 0) {
    1570              :         // Definition of COP:           COP = Qload/Power, therefore Power = Qload/COP
    1571              :         // Energy balance:              Qhr = Qload + Power = Qload(1 + 1/COP), cooling mode (recovers hot water)
    1572              :         //                              Qhr = Qload - Power = Qload(1 - 1/COP), heating mode (recovers chilled water)
    1573            6 :         Real64 designHeatRecoveryHeatTransfer = 0.0;
    1574            6 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1575            3 :             designHeatRecoveryHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
    1576              :         } else {
    1577            3 :             designHeatRecoveryHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
    1578              :         }
    1579              :         // calculate the design heat recovery flow rate, by applying the sensible heat rate equation:
    1580            6 :         tmpHeatRecoveryVolFlow = designHeatRecoveryHeatTransfer / (state.dataSize->PlantSizData(plantHRSizingIndex).DeltaT * CpHR * rhoHR);
    1581              :         // not sure about this
    1582              :         // if (this->airSource && this->heatRecoveryHeatPump) {
    1583              :         //    // If component is on plant outlet branch, use plant flow rate
    1584              :         //    tmpHeatRecoveryVolFlow = state.dataSize->PlantSizData(plantHRSizingIndex).DesVolFlowRate;
    1585              :         //}
    1586              :     } else {
    1587              :         // set it to the load side if there is plant sizing information
    1588            0 :         tmpHeatRecoveryVolFlow = tmpLoadVolFlow;
    1589              :     }
    1590              :     // check if the sizing ratio is based on the this->EIRHPType
    1591            6 :     if (this->companionHeatPumpCoil) {
    1592            6 :         tmpHeatRecoveryVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
    1593              :     } else {
    1594            0 :         tmpHeatRecoveryVolFlow *= this->heatSizingRatio;
    1595              :     }
    1596            6 :     this->heatRecoveryDesignMassFlowRate = rhoHR * this->heatRecoveryDesignVolFlowRate;
    1597              : 
    1598            6 :     if (this->heatRecoveryDesignVolFlowRateWasAutoSized) {
    1599            6 :         this->heatRecoveryDesignVolFlowRate = tmpHeatRecoveryVolFlow;
    1600            6 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1601            6 :             BaseSizer::reportSizerOutput(
    1602              :                 state, typeName, this->name, "Design Size Heat Recovery Side Volume Flow Rate [m3/s]", tmpHeatRecoveryVolFlow);
    1603              :         }
    1604            6 :         if (state.dataPlnt->PlantFirstSizesOkayToReport) {
    1605            6 :             BaseSizer::reportSizerOutput(
    1606              :                 state, typeName, this->name, "Initial Design Size Heat Recovery Side Volume Flow Rate [m3/s]", tmpHeatRecoveryVolFlow);
    1607              :         }
    1608              :     } else {
    1609              :         // heat recovery design volume flow rate was hard-sized
    1610            0 :         if (this->heatRecoveryDesignVolFlowRate > 0.0 && tmpHeatRecoveryVolFlow > 0.0) {
    1611            0 :             Real64 const hardSizedHeatRecoveryFlow = this->heatRecoveryDesignVolFlowRate;
    1612            0 :             if (state.dataPlnt->PlantFinalSizesOkayToReport) {
    1613            0 :                 if (state.dataGlobal->DoPlantSizing) {
    1614            0 :                     BaseSizer::reportSizerOutput(state,
    1615              :                                                  typeName,
    1616              :                                                  this->name,
    1617              :                                                  "Design Size Heat Recovery Side Volume Flow Rate [m3/s]",
    1618              :                                                  tmpHeatRecoveryVolFlow,
    1619              :                                                  "User-Specified Heat Recovery Side Volume Flow Rate [m3/s]",
    1620              :                                                  hardSizedHeatRecoveryFlow);
    1621              :                 } else {
    1622            0 :                     BaseSizer::reportSizerOutput(
    1623              :                         state, typeName, this->name, "User-Specified Heat Recovery Side Volume Flow Rate [m3/s]", hardSizedHeatRecoveryFlow);
    1624              :                 }
    1625            0 :                 if (state.dataGlobal->DisplayExtraWarnings) {
    1626            0 :                     if ((std::abs(tmpHeatRecoveryVolFlow - hardSizedHeatRecoveryFlow) / hardSizedHeatRecoveryFlow) >
    1627            0 :                         state.dataSize->AutoVsHardSizingThreshold) {
    1628            0 :                         ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
    1629            0 :                         ShowContinueError(state,
    1630            0 :                                           format("User-Specified Heat Recovery Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedHeatRecoveryFlow));
    1631            0 :                         ShowContinueError(
    1632            0 :                             state, format("differs from Design Size Heat Recovery Side Volume Flow Rate of {:.2R} [m3/s]", tmpHeatRecoveryVolFlow));
    1633            0 :                         ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1634            0 :                         ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1635              :                     }
    1636              :                 }
    1637              :             }
    1638              :         }
    1639              :     }
    1640              : }
    1641              : 
    1642           56 : PlantComponent *EIRPlantLoopHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type, const std::string &hp_name)
    1643              : {
    1644           56 :     if (state.dataEIRPlantLoopHeatPump->getInputsPLHP) {
    1645           40 :         EIRPlantLoopHeatPump::processInputForEIRPLHP(state);
    1646           38 :         EIRPlantLoopHeatPump::pairUpCompanionCoils(state);
    1647           38 :         state.dataEIRPlantLoopHeatPump->getInputsPLHP = false;
    1648              :     }
    1649              : 
    1650           80 :     for (auto &plhp : state.dataEIRPlantLoopHeatPump->heatPumps) {
    1651           64 :         if (plhp.name == Util::makeUPPER(hp_name) && plhp.EIRHPType == hp_type) {
    1652           38 :             return &plhp;
    1653              :         }
    1654           92 :     }
    1655              : 
    1656           32 :     ShowFatalError(state, format("EIR Plant Loop Heat Pump factory: Error getting inputs for PLHP named: {}", hp_name));
    1657              :     return nullptr; // LCOV_EXCL_LINE
    1658              : }
    1659              : 
    1660           41 : void EIRPlantLoopHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
    1661              : {
    1662           95 :     for (auto &thisHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
    1663           56 :         if (!thisHP.companionCoilName.empty()) {
    1664           32 :             std::string const thisCoilName = Util::makeUPPER(thisHP.name);
    1665           32 :             DataPlant::PlantEquipmentType thisCoilType = thisHP.EIRHPType;
    1666           32 :             std::string const targetCompanionName = Util::makeUPPER(thisHP.companionCoilName);
    1667           50 :             for (auto &potentialCompanionCoil : state.dataEIRPlantLoopHeatPump->heatPumps) {
    1668           49 :                 DataPlant::PlantEquipmentType potentialCompanionType = potentialCompanionCoil.EIRHPType;
    1669           49 :                 std::string potentialCompanionName = Util::makeUPPER(potentialCompanionCoil.name);
    1670           49 :                 if (potentialCompanionName == thisCoilName) {
    1671              :                     // skip the current coil
    1672           17 :                     continue;
    1673              :                 }
    1674           32 :                 if (potentialCompanionName == targetCompanionName) {
    1675           31 :                     if (thisCoilType == potentialCompanionType) {
    1676            1 :                         ShowSevereError(state, format("Invalid companion specification for EIR Plant Loop Heat Pump named \"{}\"", thisCoilName));
    1677            2 :                         ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
    1678            3 :                         ShowFatalError(state, "Invalid companion object causes program termination");
    1679              :                     }
    1680           30 :                     thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
    1681           30 :                     break;
    1682              :                 }
    1683           82 :             }
    1684           31 :             if (!thisHP.companionHeatPumpCoil) {
    1685            2 :                 ShowSevereError(state, "Could not find matching companion heat pump coil.");
    1686            1 :                 ShowContinueError(state, format("Base coil: {}", thisCoilName));
    1687            1 :                 ShowContinueError(state, format("Looking for companion coil named: {}", targetCompanionName));
    1688            3 :                 ShowFatalError(state, "Simulation aborts due to previous severe error");
    1689              :             }
    1690           34 :         }
    1691           43 :     }
    1692           39 : }
    1693              : 
    1694           40 : void EIRPlantLoopHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
    1695              : {
    1696              : 
    1697              :     struct ClassType
    1698              :     {
    1699              :         DataPlant::PlantEquipmentType thisType;
    1700              :         std::string nodesType;
    1701              :         std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
    1702              :         std::function<Real64(Real64, Real64)> calcQsource;
    1703              :         std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
    1704              :         std::function<Real64(Real64, Real64)> calcQheatRecovery;
    1705              :         std::function<Real64(Real64, Real64)> calcHROutletTemp;
    1706              : 
    1707           80 :         ClassType(DataPlant::PlantEquipmentType _thisType,
    1708              :                   std::string _nodesType,
    1709              :                   std::function<Real64(Real64, Real64)> _tLoadOutFunc,
    1710              :                   std::function<Real64(Real64, Real64)> _qSrcFunc,
    1711              :                   std::function<Real64(Real64, Real64)> _tSrcOutFunc,
    1712              :                   std::function<Real64(Real64, Real64)> _qHeatRecovery,
    1713              :                   std::function<Real64(Real64, Real64)> _tHROutFunc)
    1714           80 :             : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(_tLoadOutFunc), calcQsource(_qSrcFunc),
    1715           80 :               calcSourceOutletTemp(_tSrcOutFunc), calcQheatRecovery(_qHeatRecovery), calcHROutletTemp(_tHROutFunc)
    1716              :         {
    1717           80 :         }
    1718              :     };
    1719              :     std::array<ClassType, 2> classesToInput = {ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRCooling,
    1720              :                                                          "Chilled Water Nodes",
    1721              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
    1722              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
    1723              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
    1724              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
    1725              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add},
    1726              :                                                ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRHeating,
    1727              :                                                          "Hot Water Nodes",
    1728              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
    1729              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
    1730              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
    1731              :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
    1732           80 :                                                          EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract}};
    1733              : 
    1734           40 :     bool errorsFound = false;
    1735           40 :     std::string &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
    1736          120 :     for (auto const &classToInput : classesToInput) {
    1737           80 :         cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
    1738              :         DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
    1739           80 :             getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(cCurrentModuleObject)));
    1740           80 :         int numPLHP = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
    1741           80 :         if (numPLHP > 0) {
    1742           54 :             auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
    1743           54 :             if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
    1744            0 :                 continue;
    1745              :             }
    1746           54 :             auto &instancesValue = instances.value();
    1747           54 :             auto const &schemaProps = state.dataInputProcessing->inputProcessor->getObjectSchemaProps(state, cCurrentModuleObject);
    1748          108 :             for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
    1749           54 :                 auto const &fields = instance.value();
    1750           54 :                 std::string const &thisObjectName = instance.key();
    1751           54 :                 state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
    1752              : 
    1753           54 :                 EIRPlantLoopHeatPump thisPLHP;
    1754           54 :                 thisPLHP.EIRHPType = classToInput.thisType;
    1755           54 :                 thisPLHP.name = Util::makeUPPER(thisObjectName);
    1756          108 :                 std::string loadSideInletNodeName = Util::makeUPPER(fields.at("load_side_inlet_node_name").get<std::string>());
    1757          108 :                 std::string loadSideOutletNodeName = Util::makeUPPER(fields.at("load_side_outlet_node_name").get<std::string>());
    1758          108 :                 std::string condenserType = Util::makeUPPER(fields.at("condenser_type").get<std::string>());
    1759          108 :                 std::string sourceSideInletNodeName = Util::makeUPPER(fields.at("source_side_inlet_node_name").get<std::string>());
    1760           54 :                 std::string sourceSideOutletNodeName = Util::makeUPPER(fields.at("source_side_outlet_node_name").get<std::string>());
    1761              :                 thisPLHP.companionCoilName =
    1762          108 :                     Util::makeUPPER(state.dataInputProcessing->inputProcessor->getAlphaFieldValue(fields, schemaProps, "companion_heat_pump_name"));
    1763              : 
    1764           54 :                 thisPLHP.loadSideDesignVolFlowRate =
    1765          108 :                     state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "load_side_reference_flow_rate");
    1766           54 :                 if (thisPLHP.loadSideDesignVolFlowRate == DataSizing::AutoSize) {
    1767           16 :                     thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
    1768              :                 }
    1769              : 
    1770           54 :                 thisPLHP.sourceSideDesignVolFlowRate =
    1771          108 :                     state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "source_side_reference_flow_rate");
    1772           54 :                 if (thisPLHP.sourceSideDesignVolFlowRate == DataSizing::AutoSize) {
    1773           24 :                     thisPLHP.sourceSideDesignVolFlowRateWasAutoSized = true;
    1774              :                 }
    1775              : 
    1776          108 :                 thisPLHP.referenceCapacity = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "reference_capacity");
    1777           54 :                 if (thisPLHP.referenceCapacity == DataSizing::AutoSize) {
    1778           26 :                     thisPLHP.referenceCapacityWasAutoSized = true;
    1779              :                 }
    1780              : 
    1781           54 :                 thisPLHP.referenceCOP =
    1782          108 :                     state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "reference_coefficient_of_performance");
    1783              : 
    1784          162 :                 thisPLHP.sizingFactor = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "sizing_factor");
    1785              : 
    1786           54 :                 std::string const capFtName = Util::makeUPPER(fields.at("capacity_modifier_function_of_temperature_curve_name").get<std::string>());
    1787           54 :                 thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, capFtName);
    1788           54 :                 if (thisPLHP.capFuncTempCurveIndex == 0) {
    1789            1 :                     ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, capFtName));
    1790            1 :                     errorsFound = true;
    1791              :                 }
    1792              : 
    1793              :                 std::string const eirFtName =
    1794           54 :                     Util::makeUPPER(fields.at("electric_input_to_output_ratio_modifier_function_of_temperature_curve_name").get<std::string>());
    1795           54 :                 thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, eirFtName);
    1796           54 :                 if (thisPLHP.powerRatioFuncTempCurveIndex == 0) {
    1797            1 :                     ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, eirFtName));
    1798            1 :                     errorsFound = true;
    1799              :                 }
    1800              : 
    1801              :                 std::string const eirFplrName =
    1802           54 :                     Util::makeUPPER(fields.at("electric_input_to_output_ratio_modifier_function_of_part_load_ratio_curve_name").get<std::string>());
    1803           54 :                 thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, eirFplrName);
    1804           54 :                 if (thisPLHP.powerRatioFuncPLRCurveIndex == 0) {
    1805            1 :                     ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, eirFplrName));
    1806            1 :                     errorsFound = true;
    1807              :                 }
    1808              : 
    1809              :                 // inputs are past min-fields
    1810              :                 // fields common to both objects
    1811          108 :                 thisPLHP.minimumPLR = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "minimum_part_load_ratio");
    1812           54 :                 thisPLHP.minSourceTempLimit =
    1813          108 :                     state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "minimum_source_inlet_temperature");
    1814           54 :                 thisPLHP.maxSourceTempLimit =
    1815          162 :                     state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "maximum_source_inlet_temperature");
    1816              : 
    1817           54 :                 auto const minimumSupplyWaterTempCurveName = fields.find("minimum_supply_water_temperature_curve_name");
    1818           54 :                 if (minimumSupplyWaterTempCurveName != fields.end()) {
    1819            0 :                     thisPLHP.minSupplyWaterTempCurveIndex =
    1820            0 :                         Curve::GetCurveIndex(state, Util::makeUPPER(minimumSupplyWaterTempCurveName.value().get<std::string>()));
    1821              :                 }
    1822              : 
    1823           54 :                 auto const maximumSupplyWaterTempCurveName = fields.find("maximum_supply_water_temperature_curve_name");
    1824           54 :                 if (maximumSupplyWaterTempCurveName != fields.end()) {
    1825            0 :                     thisPLHP.maxSupplyWaterTempCurveIndex =
    1826            0 :                         Curve::GetCurveIndex(state, Util::makeUPPER(maximumSupplyWaterTempCurveName.value().get<std::string>()));
    1827              :                 }
    1828              :                 // fields only in cooling object
    1829           54 :                 if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
    1830           33 :                     auto const thermosiphonTempCurveName = fields.find("thermosiphon_capacity_fraction_curve_name");
    1831           33 :                     if (thermosiphonTempCurveName != fields.end()) {
    1832            1 :                         thisPLHP.thermosiphonTempCurveIndex =
    1833            1 :                             Curve::GetCurveIndex(state, Util::makeUPPER(thermosiphonTempCurveName.value().get<std::string>()));
    1834            1 :                         if (thisPLHP.thermosiphonTempCurveIndex == 0) {
    1835            0 :                             ShowSevereError(state, format("{} =\"{}\"", state.dataIPShortCut->cCurrentModuleObject, thisPLHP.name));
    1836            0 :                             ShowContinueError(state,
    1837            0 :                                               format("Invalid Thermosiphon Capacity Fraction Curve Name = {}",
    1838            0 :                                                      thermosiphonTempCurveName.value().get<std::string>()));
    1839            0 :                             errorsFound = true;
    1840              :                         }
    1841              :                     }
    1842           66 :                     thisPLHP.thermosiphonMinTempDiff = state.dataInputProcessing->inputProcessor->getRealFieldValue(
    1843              :                         fields, schemaProps, "thermosiphon_minimum_temperature_difference");
    1844           33 :                 }
    1845              : 
    1846              :                 std::string flowModeTypeName =
    1847          108 :                     Util::makeUPPER(state.dataInputProcessing->inputProcessor->getAlphaFieldValue(fields, schemaProps, "flow_mode"));
    1848           54 :                 thisPLHP.flowMode = static_cast<DataPlant::FlowMode>(getEnumValue(DataPlant::FlowModeNamesUC, flowModeTypeName));
    1849              : 
    1850              :                 // fields only in heating object
    1851           54 :                 if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1852           21 :                     thisPLHP.heatSizingRatio =
    1853           42 :                         state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "heating_to_cooling_capacity_sizing_ratio");
    1854           63 :                     thisPLHP.maxOutdoorTemperatureDefrost = state.dataInputProcessing->inputProcessor->getRealFieldValue(
    1855              :                         fields, schemaProps, "maximum_outdoor_dry_bulb_temperature_for_defrost_operation");
    1856              :                 }
    1857              : 
    1858           54 :                 constexpr std::array<std::string_view, static_cast<int>(HeatSizingType::Num)> PLHPHeatSizTypeNamesUC = {
    1859              :                     "HEATINGCAPACITY", "COOLINGCAPACITY", "GREATEROFHEATINGORCOOLING"};
    1860           54 :                 auto const heatSizingType = fields.find("heat_pump_sizing_method");
    1861           54 :                 if (heatSizingType != fields.end()) {
    1862            0 :                     thisPLHP.heatSizingMethod =
    1863            0 :                         static_cast<HeatSizingType>(getEnumValue(PLHPHeatSizTypeNamesUC, Util::makeUPPER(heatSizingType.value().get<std::string>())));
    1864              :                 } else {
    1865              :                     // revert to legacy sizing method, if no companion coil and this coil type is heating, set to heating
    1866           54 :                     if (thisPLHP.companionCoilName.empty() && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    1867            7 :                         thisPLHP.heatSizingMethod = HeatSizingType::Heating;
    1868              :                     } else {
    1869           47 :                         thisPLHP.heatSizingMethod = HeatSizingType::Cooling;
    1870              :                     }
    1871              :                 }
    1872              : 
    1873           54 :                 constexpr std::array<std::string_view, static_cast<int>(ControlType::Num)> PLHPCtrlTypeNamesUC = {"SETPOINT", "LOAD"};
    1874           54 :                 auto const controlType = fields.find("control_type");
    1875           54 :                 if (controlType != fields.end()) {
    1876            0 :                     thisPLHP.sysControlType =
    1877            0 :                         static_cast<ControlType>(getEnumValue(PLHPCtrlTypeNamesUC, Util::makeUPPER(controlType.value().get<std::string>())));
    1878              :                 } else {
    1879           54 :                     thisPLHP.sysControlType = ControlType::Load;
    1880              :                 }
    1881           54 :                 auto const capacityDryAirCurveName = fields.find("dry_outdoor_correction_factor_curve_name");
    1882           54 :                 if (capacityDryAirCurveName != fields.end()) {
    1883            0 :                     thisPLHP.capacityDryAirCurveIndex =
    1884            0 :                         Curve::GetCurveIndex(state, Util::makeUPPER(capacityDryAirCurveName.value().get<std::string>()));
    1885              :                 }
    1886              : 
    1887           54 :                 constexpr std::array<std::string_view, static_cast<int>(DefrostControl::Num)> PLHPDefrostTypeNamesUC = {
    1888              :                     "NONE", "TIMED", "ONDEMAND", "TIMEDEMPIRICAL"};
    1889           54 :                 auto const defrostControlStrategy = fields.find("heat_pump_defrost_control");
    1890           54 :                 if (defrostControlStrategy != fields.end()) {
    1891            0 :                     thisPLHP.defrostStrategy = static_cast<DefrostControl>(
    1892            0 :                         getEnumValue(PLHPDefrostTypeNamesUC, Util::makeUPPER(defrostControlStrategy.value().get<std::string>())));
    1893              :                 } else {
    1894           54 :                     thisPLHP.defrostStrategy = DefrostControl::None;
    1895              :                 }
    1896              : 
    1897           54 :                 if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating &&
    1898           21 :                     (thisPLHP.defrostStrategy == DefrostControl::Timed || thisPLHP.defrostStrategy == DefrostControl::TimedEmpirical)) {
    1899            0 :                     auto const timePeriod = fields.find("heat_pump_defrost_time_period_fraction");
    1900            0 :                     if (timePeriod != fields.end()) {
    1901            0 :                         thisPLHP.defrostTime = timePeriod.value().get<Real64>();
    1902              :                     } else {
    1903            0 :                         Real64 defaultVal = 0.0;
    1904            0 :                         if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    1905              :                                 state, cCurrentModuleObject, "heat_pump_defrost_time_period_fraction", defaultVal)) {
    1906              :                             // excluding from coverage
    1907              :                             ShowSevereError(state, // LCOV_EXCL_LINE
    1908            0 :                                             format("EIR PLHP \"{}\": Heat Pump Defrost Time Period Fraction not entered and default value not found.",
    1909              :                                                    thisPLHP.name)); // LCOV_EXCL_LINE
    1910              :                             errorsFound = true;                     // LCOV_EXCL_LINE
    1911              :                         } else {
    1912            0 :                             thisPLHP.defrostTime = defaultVal;
    1913              :                         }
    1914              :                     }
    1915            0 :                 }
    1916              : 
    1917           54 :                 if (thisPLHP.defrostStrategy == DefrostControl::TimedEmpirical) {
    1918            0 :                     auto const timedEmpiricalDefFreqStratCurveName = fields.find("timed_empirical_defrost_frequency_curve_name");
    1919            0 :                     if (timedEmpiricalDefFreqStratCurveName != fields.end()) {
    1920            0 :                         thisPLHP.defrostFreqCurveIndex =
    1921            0 :                             Curve::GetCurveIndex(state, Util::makeUPPER(timedEmpiricalDefFreqStratCurveName.value().get<std::string>()));
    1922              :                     }
    1923            0 :                     auto const timedEmpiricalDefHeatLoadPenaltyCurveName = fields.find("timed_empirical_defrost_heat_load_penalty_curve_name");
    1924            0 :                     if (timedEmpiricalDefHeatLoadPenaltyCurveName != fields.end()) {
    1925            0 :                         thisPLHP.defrostHeatLoadCurveIndex =
    1926            0 :                             Curve::GetCurveIndex(state, Util::makeUPPER(timedEmpiricalDefHeatLoadPenaltyCurveName.value().get<std::string>()));
    1927            0 :                         thisPLHP.defrostLoadCurveDims = state.dataCurveManager->curves(thisPLHP.defrostHeatLoadCurveIndex)->numDims;
    1928              :                     }
    1929            0 :                     auto const defrostHeatEnergyCurveIndexCurveName = fields.find("timed_empirical_defrost_heat_input_energy_fraction_curve_name");
    1930            0 :                     if (defrostHeatEnergyCurveIndexCurveName != fields.end()) {
    1931            0 :                         thisPLHP.defrostHeatEnergyCurveIndex =
    1932            0 :                             Curve::GetCurveIndex(state, Util::makeUPPER(defrostHeatEnergyCurveIndexCurveName.value().get<std::string>()));
    1933            0 :                         thisPLHP.defrostEnergyCurveDims = state.dataCurveManager->curves(thisPLHP.defrostHeatEnergyCurveIndex)->numDims;
    1934              :                     }
    1935           54 :                 } else if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // used for Timed or OnDemand
    1936           21 :                     auto const defEIRFTCurveName = fields.find("defrost_energy_input_ratio_function_of_temperature_curve_name");
    1937           21 :                     if (defEIRFTCurveName != fields.end()) {
    1938            0 :                         thisPLHP.defrostEIRFTIndex = Curve::GetCurveIndex(state, Util::makeUPPER(defEIRFTCurveName.value().get<std::string>()));
    1939              :                     }
    1940           21 :                 }
    1941              : 
    1942           54 :                 bool nodeErrorsFound = false;
    1943           54 :                 thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    1944              :                                                                                    loadSideInletNodeName,
    1945              :                                                                                    nodeErrorsFound,
    1946              :                                                                                    objType,
    1947              :                                                                                    thisPLHP.name,
    1948              :                                                                                    DataLoopNode::NodeFluidType::Water,
    1949              :                                                                                    DataLoopNode::ConnectionType::Inlet,
    1950              :                                                                                    NodeInputManager::CompFluidStream::Primary,
    1951              :                                                                                    DataLoopNode::ObjectIsNotParent);
    1952           54 :                 thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    1953              :                                                                                     loadSideOutletNodeName,
    1954              :                                                                                     nodeErrorsFound,
    1955              :                                                                                     objType,
    1956              :                                                                                     thisPLHP.name,
    1957              :                                                                                     DataLoopNode::NodeFluidType::Water,
    1958              :                                                                                     DataLoopNode::ConnectionType::Outlet,
    1959              :                                                                                     NodeInputManager::CompFluidStream::Primary,
    1960              :                                                                                     DataLoopNode::ObjectIsNotParent);
    1961           54 :                 DataLoopNode::NodeFluidType condenserNodeType = DataLoopNode::NodeFluidType::Blank;
    1962           54 :                 DataLoopNode::ConnectionType condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Blank;
    1963           54 :                 DataLoopNode::ConnectionType condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Blank;
    1964           54 :                 if (condenserType == "WATERSOURCE") {
    1965           29 :                     thisPLHP.waterSource = true;
    1966           29 :                     condenserNodeType = DataLoopNode::NodeFluidType::Water;
    1967           29 :                     condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
    1968           29 :                     condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
    1969           25 :                 } else if (condenserType == "AIRSOURCE") {
    1970           25 :                     thisPLHP.airSource = true;
    1971           25 :                     condenserNodeType = DataLoopNode::NodeFluidType::Air;
    1972           25 :                     condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
    1973           25 :                     condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
    1974           25 :                     if (sourceSideInletNodeName == sourceSideOutletNodeName) {
    1975            1 :                         ShowSevereError(state, format("PlantLoopHeatPump {} has the same inlet and outlet node.", thisObjectName));
    1976            1 :                         ShowContinueError(state, format("Node Name: {}", sourceSideInletNodeName));
    1977            1 :                         errorsFound = true;
    1978              :                     }
    1979              :                 } else {
    1980              :                     // Again, this should be protected by the input processor
    1981            0 :                     ShowErrorMessage(
    1982              :                         state, format("Invalid heat pump condenser type (name={}; entered type: {}", thisPLHP.name, condenserType)); // LCOV_EXCL_LINE
    1983              :                     errorsFound = true;                                                                                              // LCOV_EXCL_LINE
    1984              :                 }
    1985           54 :                 thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    1986              :                                                                                      sourceSideInletNodeName,
    1987              :                                                                                      nodeErrorsFound,
    1988              :                                                                                      objType,
    1989              :                                                                                      thisPLHP.name,
    1990              :                                                                                      condenserNodeType,
    1991              :                                                                                      condenserNodeConnectionType_Inlet,
    1992              :                                                                                      NodeInputManager::CompFluidStream::Secondary,
    1993              :                                                                                      DataLoopNode::ObjectIsNotParent);
    1994           54 :                 thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    1995              :                                                                                       sourceSideOutletNodeName,
    1996              :                                                                                       nodeErrorsFound,
    1997              :                                                                                       objType,
    1998              :                                                                                       thisPLHP.name,
    1999              :                                                                                       condenserNodeType,
    2000              :                                                                                       condenserNodeConnectionType_Outlet,
    2001              :                                                                                       NodeInputManager::CompFluidStream::Secondary,
    2002              :                                                                                       DataLoopNode::ObjectIsNotParent);
    2003              : 
    2004              :                 // heat recovery inputs
    2005           54 :                 std::string heatRecoveryInletNodeName;
    2006           54 :                 std::string heatRecoveryOutletNodeName;
    2007          108 :                 auto const hrInletNodeName = fields.find("heat_recovery_inlet_node_name");
    2008           54 :                 auto const hrOutletNodeName = fields.find("heat_recovery_outlet_node_name");
    2009           54 :                 if (hrInletNodeName != fields.end() && hrOutletNodeName != fields.end()) {
    2010           16 :                     heatRecoveryInletNodeName = Util::makeUPPER(fields.at("heat_recovery_inlet_node_name").get<std::string>());
    2011            8 :                     heatRecoveryOutletNodeName = Util::makeUPPER(fields.at("heat_recovery_outlet_node_name").get<std::string>());
    2012            8 :                     thisPLHP.heatRecoveryAvailable = true;
    2013              :                 } else {
    2014           46 :                     thisPLHP.heatRecoveryAvailable = false;
    2015              :                 }
    2016              : 
    2017           54 :                 if (thisPLHP.airSource && thisPLHP.heatRecoveryAvailable) {
    2018            8 :                     thisPLHP.heatRecoveryNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    2019              :                                                                                            heatRecoveryInletNodeName,
    2020              :                                                                                            nodeErrorsFound,
    2021              :                                                                                            objType,
    2022              :                                                                                            thisPLHP.name,
    2023              :                                                                                            DataLoopNode::NodeFluidType::Water,
    2024              :                                                                                            DataLoopNode::ConnectionType::Inlet,
    2025              :                                                                                            NodeInputManager::CompFluidStream::Tertiary,
    2026              :                                                                                            DataLoopNode::ObjectIsNotParent);
    2027            8 :                     thisPLHP.heatRecoveryNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    2028              :                                                                                             heatRecoveryOutletNodeName,
    2029              :                                                                                             nodeErrorsFound,
    2030              :                                                                                             objType,
    2031              :                                                                                             thisPLHP.name,
    2032              :                                                                                             DataLoopNode::NodeFluidType::Water,
    2033              :                                                                                             DataLoopNode::ConnectionType::Outlet,
    2034              :                                                                                             NodeInputManager::CompFluidStream::Tertiary,
    2035              :                                                                                             DataLoopNode::ObjectIsNotParent);
    2036              : 
    2037            8 :                     thisPLHP.heatRecoveryDesignVolFlowRate =
    2038           16 :                         state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "heat_recovery_reference_flow_rate");
    2039            8 :                     if (thisPLHP.heatRecoveryDesignVolFlowRate == DataSizing::AutoSize) {
    2040            8 :                         thisPLHP.heatRecoveryDesignVolFlowRateWasAutoSized = true;
    2041              :                     }
    2042              : 
    2043              :                     // fields only in cooling object
    2044            8 :                     if (thisPLHP.heatRecoveryAvailable && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
    2045           12 :                         thisPLHP.maxHeatRecoveryTempLimit = state.dataInputProcessing->inputProcessor->getRealFieldValue(
    2046              :                             fields, schemaProps, "maximum_heat_recovery_outlet_temperature");
    2047              :                     }
    2048              :                     // fields only in heating object
    2049            8 :                     if (thisPLHP.heatRecoveryAvailable && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    2050           12 :                         thisPLHP.minHeatRecoveryTempLimit = state.dataInputProcessing->inputProcessor->getRealFieldValue(
    2051              :                             fields, schemaProps, "minimum_heat_recovery_outlet_temperature");
    2052              :                     }
    2053              :                 }
    2054              : 
    2055           54 :                 if (nodeErrorsFound) {
    2056            0 :                     errorsFound = true;
    2057              :                 }
    2058           54 :                 BranchNodeConnections::TestCompSet(
    2059           54 :                     state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
    2060              : 
    2061           54 :                 if (thisPLHP.waterSource) {
    2062           58 :                     BranchNodeConnections::TestCompSet(
    2063              :                         state, cCurrentModuleObject, thisPLHP.name, sourceSideInletNodeName, sourceSideOutletNodeName, "Condenser Water Nodes");
    2064              :                 }
    2065              : 
    2066           54 :                 if (thisPLHP.airSource && thisPLHP.heatRecoveryAvailable) {
    2067           16 :                     BranchNodeConnections::TestCompSet(state,
    2068              :                                                        cCurrentModuleObject,
    2069              :                                                        thisPLHP.name,
    2070              :                                                        heatRecoveryInletNodeName,
    2071              :                                                        heatRecoveryOutletNodeName,
    2072              :                                                        "Heat Recovery Water Nodes");
    2073              : 
    2074            8 :                     auto const heatRecoveryCapFTempCurveName = fields.find("heat_recovery_capacity_modifier_function_of_temperature_curve_name");
    2075            8 :                     if (heatRecoveryCapFTempCurveName != fields.end()) {
    2076            0 :                         thisPLHP.heatRecoveryCapFTempCurveIndex =
    2077            0 :                             Curve::GetCurveIndex(state, Util::makeUPPER(heatRecoveryCapFTempCurveName.value().get<std::string>()));
    2078              :                     }
    2079              :                     auto const heatRecoveryEIRFTempCurveName =
    2080            8 :                         fields.find("heat_recovery_electric_input_to_output_ratio_modifier_function_of_temperature_curve_name");
    2081            8 :                     if (heatRecoveryEIRFTempCurveName != fields.end()) {
    2082            0 :                         thisPLHP.heatRecoveryEIRFTempCurveIndex =
    2083            0 :                             Curve::GetCurveIndex(state, Util::makeUPPER(heatRecoveryEIRFTempCurveName.value().get<std::string>()));
    2084              :                     }
    2085            8 :                 }
    2086              : 
    2087           54 :                 if (thisPLHP.airSource && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating &&
    2088            9 :                     thisPLHP.defrostStrategy != DefrostControl::None) {
    2089            0 :                     thisPLHP.defrostAvailable = true;
    2090              :                 }
    2091              :                 // store the worker functions that generalized the heating/cooling sides
    2092           54 :                 thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
    2093           54 :                 thisPLHP.calcQsource = classToInput.calcQsource;
    2094           54 :                 thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
    2095              :                 // heat recovery
    2096           54 :                 thisPLHP.calcQheatRecovery = classToInput.calcQheatRecovery;
    2097           54 :                 thisPLHP.calcHROutletTemp = classToInput.calcHROutletTemp;
    2098              : 
    2099           54 :                 if (!errorsFound) {
    2100           52 :                     state.dataEIRPlantLoopHeatPump->heatPumps.push_back(thisPLHP);
    2101              :                 }
    2102          108 :             }
    2103           54 :         }
    2104              :     }
    2105           40 :     if (errorsFound) {
    2106              :         // currently there are no straightforward unit tests possible to get here
    2107              :         // all curves are required and inputs are validated by the input processor
    2108              :         // obviously this will stay here but I don't feel like counting it against coverage
    2109              :         ShowFatalError(state, "Previous EIR PLHP errors cause program termination"); // LCOV_EXCL_LINE
    2110              :     }
    2111           40 : }
    2112              : 
    2113        32525 : void EIRPlantLoopHeatPump::checkConcurrentOperation(EnergyPlusData &state)
    2114              : {
    2115              :     // This will do a recurring warning for concurrent companion operation.
    2116              :     // This function should be called at the end of the time-step to ensure any iteration-level operation
    2117              :     //  is worked out and the results are final.
    2118              :     // This function does not try to be intelligent about only reporting for one of the companions.  The only
    2119              :     //  way I could think of was to have a vector, either static here or in the namespace, that would hold
    2120              :     //  companion index values as I warn against their partner, so then I would have to add the values to the
    2121              :     //  vector each pass, and check then each loop.  This seemed really bulky and inefficient, so I chose to
    2122              :     //  leave a tight loop here of just reporting for each coil if it and the companion are running.
    2123        32529 :     for (auto &thisPLHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
    2124            4 :         if (!thisPLHP.companionHeatPumpCoil) {
    2125            2 :             continue;
    2126              :         }
    2127            2 :         if (thisPLHP.running && thisPLHP.companionHeatPumpCoil->running && !thisPLHP.companionHeatPumpCoil->heatRecoveryAvailable) {
    2128           16 :             ShowRecurringWarningErrorAtEnd(state,
    2129            2 :                                            "Companion heat pump objects running concurrently, check operation.  Base object name: " + thisPLHP.name,
    2130            2 :                                            thisPLHP.recurringConcurrentOperationWarningIndex);
    2131              :         }
    2132        32525 :     }
    2133        32525 : }
    2134              : 
    2135           36 : void EIRPlantLoopHeatPump::isPlantInletOrOutlet(EnergyPlusData &state)
    2136              : {
    2137              :     // check to see if component is on a plant inlet or outlet branch to determine if flow should be registered
    2138              :     // only components on plant parallel component branches should be registered
    2139              :     // this check for the load side on a plant inlet branch and source side on a plant outlet branch
    2140              :     // likely will need more checking here but this works for now with existing test file
    2141           36 :     bool loadSideIsPlantInlet = false;
    2142           36 :     bool sourceSideIsPlantOutlet = false;
    2143           93 :     for (auto thisPlant : state.dataPlnt->PlantLoop) {
    2144          171 :         for (auto thisLoopSide : thisPlant.LoopSide) {
    2145          114 :             if (this->loadSideNodes.inlet == thisLoopSide.NodeNumIn) {
    2146            0 :                 loadSideIsPlantInlet = true;
    2147              :             }
    2148          114 :             if (this->sourceSideNodes.outlet == thisLoopSide.NodeNumOut) {
    2149            0 :                 sourceSideIsPlantOutlet = true;
    2150              :             }
    2151          114 :             if (loadSideIsPlantInlet && sourceSideIsPlantOutlet) {
    2152            0 :                 this->heatRecoveryHeatPump = true;
    2153            0 :                 break;
    2154              :             }
    2155          114 :         }
    2156           57 :         if (this->heatRecoveryHeatPump) {
    2157            0 :             break;
    2158              :         }
    2159           57 :     }
    2160           36 : }
    2161              : 
    2162           36 : void EIRPlantLoopHeatPump::oneTimeInit(EnergyPlusData &state)
    2163              : {
    2164              :     // This function does all the one-time initialization
    2165           36 :     constexpr std::string_view routineName = "EIRPlantLoopHeatPump : oneTimeInit"; // + __FUNCTION__;
    2166              : 
    2167           36 :     if (this->oneTimeInitFlag) {
    2168           35 :         bool errFlag = false;
    2169              : 
    2170              :         // setup output variables
    2171           70 :         SetupOutputVariable(state,
    2172              :                             "Heat Pump Part Load Ratio",
    2173              :                             Constant::Units::None,
    2174           35 :                             this->partLoadRatio,
    2175              :                             OutputProcessor::TimeStepType::System,
    2176              :                             OutputProcessor::StoreType::Average,
    2177           35 :                             this->name);
    2178           70 :         SetupOutputVariable(state,
    2179              :                             "Heat Pump Cycling Ratio",
    2180              :                             Constant::Units::None,
    2181           35 :                             this->cyclingRatio,
    2182              :                             OutputProcessor::TimeStepType::System,
    2183              :                             OutputProcessor::StoreType::Average,
    2184           35 :                             this->name);
    2185           70 :         SetupOutputVariable(state,
    2186              :                             "Heat Pump Load Side Heat Transfer Rate",
    2187              :                             Constant::Units::W,
    2188           35 :                             this->loadSideHeatTransfer,
    2189              :                             OutputProcessor::TimeStepType::System,
    2190              :                             OutputProcessor::StoreType::Average,
    2191           35 :                             this->name);
    2192           70 :         SetupOutputVariable(state,
    2193              :                             "Heat Pump Load Side Heat Transfer Energy",
    2194              :                             Constant::Units::J,
    2195           35 :                             this->loadSideEnergy,
    2196              :                             OutputProcessor::TimeStepType::System,
    2197              :                             OutputProcessor::StoreType::Sum,
    2198           35 :                             this->name,
    2199              :                             Constant::eResource::EnergyTransfer,
    2200              :                             OutputProcessor::Group::Plant);
    2201           70 :         SetupOutputVariable(state,
    2202              :                             "Heat Pump Source Side Heat Transfer Rate",
    2203              :                             Constant::Units::W,
    2204           35 :                             this->sourceSideHeatTransfer,
    2205              :                             OutputProcessor::TimeStepType::System,
    2206              :                             OutputProcessor::StoreType::Average,
    2207           35 :                             this->name);
    2208           70 :         SetupOutputVariable(state,
    2209              :                             "Heat Pump Source Side Heat Transfer Energy",
    2210              :                             Constant::Units::J,
    2211           35 :                             this->sourceSideEnergy,
    2212              :                             OutputProcessor::TimeStepType::System,
    2213              :                             OutputProcessor::StoreType::Sum,
    2214           35 :                             this->name);
    2215           70 :         SetupOutputVariable(state,
    2216              :                             "Heat Pump Load Side Inlet Temperature",
    2217              :                             Constant::Units::C,
    2218           35 :                             this->loadSideInletTemp,
    2219              :                             OutputProcessor::TimeStepType::System,
    2220              :                             OutputProcessor::StoreType::Average,
    2221           35 :                             this->name);
    2222           70 :         SetupOutputVariable(state,
    2223              :                             "Heat Pump Load Side Outlet Temperature",
    2224              :                             Constant::Units::C,
    2225           35 :                             this->loadSideOutletTemp,
    2226              :                             OutputProcessor::TimeStepType::System,
    2227              :                             OutputProcessor::StoreType::Average,
    2228           35 :                             this->name);
    2229           70 :         SetupOutputVariable(state,
    2230              :                             "Heat Pump Source Side Inlet Temperature",
    2231              :                             Constant::Units::C,
    2232           35 :                             this->sourceSideInletTemp,
    2233              :                             OutputProcessor::TimeStepType::System,
    2234              :                             OutputProcessor::StoreType::Average,
    2235           35 :                             this->name);
    2236           70 :         SetupOutputVariable(state,
    2237              :                             "Heat Pump Source Side Outlet Temperature",
    2238              :                             Constant::Units::C,
    2239           35 :                             this->sourceSideOutletTemp,
    2240              :                             OutputProcessor::TimeStepType::System,
    2241              :                             OutputProcessor::StoreType::Average,
    2242           35 :                             this->name);
    2243           70 :         SetupOutputVariable(state,
    2244              :                             "Heat Pump Electricity Rate",
    2245              :                             Constant::Units::W,
    2246           35 :                             this->powerUsage,
    2247              :                             OutputProcessor::TimeStepType::System,
    2248              :                             OutputProcessor::StoreType::Average,
    2249           35 :                             this->name);
    2250           35 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) { // energy from HeatPump:PlantLoop:EIR:Cooling object
    2251           42 :             SetupOutputVariable(state,
    2252              :                                 "Heat Pump Electricity Energy",
    2253              :                                 Constant::Units::J,
    2254           21 :                                 this->powerEnergy,
    2255              :                                 OutputProcessor::TimeStepType::System,
    2256              :                                 OutputProcessor::StoreType::Sum,
    2257           21 :                                 this->name,
    2258              :                                 Constant::eResource::Electricity,
    2259              :                                 OutputProcessor::Group::Plant,
    2260              :                                 OutputProcessor::EndUseCat::Cooling,
    2261              :                                 "Heat Pump");
    2262           21 :             SetupOutputVariable(state,
    2263              :                                 "Thermosiphon Status",
    2264              :                                 Constant::Units::None,
    2265           21 :                                 this->thermosiphonStatus,
    2266              :                                 OutputProcessor::TimeStepType::System,
    2267              :                                 OutputProcessor::StoreType::Average,
    2268           21 :                                 this->name);
    2269           14 :         } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // energy from HeatPump:PlantLoop:EIR:Heating object
    2270           28 :             SetupOutputVariable(state,
    2271              :                                 "Heat Pump Electricity Energy",
    2272              :                                 Constant::Units::J,
    2273           14 :                                 this->powerEnergy,
    2274              :                                 OutputProcessor::TimeStepType::System,
    2275              :                                 OutputProcessor::StoreType::Sum,
    2276           14 :                                 this->name,
    2277              :                                 Constant::eResource::Electricity,
    2278              :                                 OutputProcessor::Group::Plant,
    2279              :                                 OutputProcessor::EndUseCat::Heating,
    2280              :                                 "Heat Pump");
    2281           14 :             if (this->defrostAvailable) {
    2282            0 :                 SetupOutputVariable(state,
    2283              :                                     "Heat Pump Load Due To Defrost",
    2284              :                                     Constant::Units::W,
    2285            0 :                                     this->loadDueToDefrost,
    2286              :                                     OutputProcessor::TimeStepType::System,
    2287              :                                     OutputProcessor::StoreType::Average,
    2288            0 :                                     this->name);
    2289            0 :                 SetupOutputVariable(state,
    2290              :                                     "Heat Pump Fractioal Defrost Time",
    2291              :                                     Constant::Units::W,
    2292            0 :                                     this->fractionalDefrostTime,
    2293              :                                     OutputProcessor::TimeStepType::System,
    2294              :                                     OutputProcessor::StoreType::Average,
    2295            0 :                                     this->name);
    2296            0 :                 SetupOutputVariable(state,
    2297              :                                     "Heat Pump Defrost Electricity Rate",
    2298              :                                     Constant::Units::W,
    2299            0 :                                     this->defrostEnergyRate,
    2300              :                                     OutputProcessor::TimeStepType::System,
    2301              :                                     OutputProcessor::StoreType::Average,
    2302            0 :                                     this->name);
    2303            0 :                 SetupOutputVariable(state,
    2304              :                                     "Heat Pump Defrost Electricity Energy",
    2305              :                                     Constant::Units::J,
    2306            0 :                                     this->defrostEnergy,
    2307              :                                     OutputProcessor::TimeStepType::System,
    2308              :                                     OutputProcessor::StoreType::Sum,
    2309            0 :                                     this->name,
    2310              :                                     Constant::eResource::Electricity,
    2311              :                                     OutputProcessor::Group::Plant,
    2312              :                                     OutputProcessor::EndUseCat::Heating,
    2313              :                                     "Heat Pump");
    2314              :             }
    2315              :         }
    2316           70 :         SetupOutputVariable(state,
    2317              :                             "Heat Pump Load Side Mass Flow Rate",
    2318              :                             Constant::Units::kg_s,
    2319           35 :                             this->loadSideMassFlowRate,
    2320              :                             OutputProcessor::TimeStepType::System,
    2321              :                             OutputProcessor::StoreType::Average,
    2322           35 :                             this->name);
    2323           70 :         SetupOutputVariable(state,
    2324              :                             "Heat Pump Source Side Mass Flow Rate",
    2325              :                             Constant::Units::kg_s,
    2326           35 :                             this->sourceSideMassFlowRate,
    2327              :                             OutputProcessor::TimeStepType::System,
    2328              :                             OutputProcessor::StoreType::Average,
    2329           35 :                             this->name);
    2330              :         // report variable used for debugging, System Node Specific Heat can also report the node Cp
    2331              :         // added spaces to SetupOutputVariable to avoid issue with variable parsing script
    2332              :         // Setup Output Variable(state,
    2333              :         //                   "Heat Pump Source Side Specific Heat",
    2334              :         //                   Constant::Units::J_kgK,
    2335              :         //                   this->sourceSideCp,
    2336              :         //                   OutputProcessor::TimeStepType::System,
    2337              :         //                   OutputProcessor::StoreType::Average,
    2338              :         //                   this->name);
    2339              : 
    2340           35 :         if (this->heatRecoveryAvailable) {
    2341           12 :             SetupOutputVariable(state,
    2342              :                                 "Heat Pump Heat Recovery Heat Transfer Rate",
    2343              :                                 Constant::Units::W,
    2344            6 :                                 this->heatRecoveryRate,
    2345              :                                 OutputProcessor::TimeStepType::System,
    2346              :                                 OutputProcessor::StoreType::Average,
    2347            6 :                                 this->name);
    2348           12 :             SetupOutputVariable(state,
    2349              :                                 "Heat Pump Heat Recovery Heat Transfer Energy",
    2350              :                                 Constant::Units::J,
    2351            6 :                                 this->heatRecoveryEnergy,
    2352              :                                 OutputProcessor::TimeStepType::System,
    2353              :                                 OutputProcessor::StoreType::Sum,
    2354            6 :                                 this->name);
    2355              : 
    2356           12 :             SetupOutputVariable(state,
    2357              :                                 "Heat Pump Heat Recovery Inlet Temperature",
    2358              :                                 Constant::Units::C,
    2359            6 :                                 this->heatRecoveryInletTemp,
    2360              :                                 OutputProcessor::TimeStepType::System,
    2361              :                                 OutputProcessor::StoreType::Average,
    2362            6 :                                 this->name);
    2363           12 :             SetupOutputVariable(state,
    2364              :                                 "Heat Pump Heat Recovery Outlet Temperature",
    2365              :                                 Constant::Units::C,
    2366            6 :                                 this->heatRecoveryOutletTemp,
    2367              :                                 OutputProcessor::TimeStepType::System,
    2368              :                                 OutputProcessor::StoreType::Average,
    2369            6 :                                 this->name);
    2370           12 :             SetupOutputVariable(state,
    2371              :                                 "Heat Pump Heat Recovery Mass Flow Rate",
    2372              :                                 Constant::Units::kg_s,
    2373            6 :                                 this->heatRecoveryMassFlowRate,
    2374              :                                 OutputProcessor::TimeStepType::System,
    2375              :                                 OutputProcessor::StoreType::Average,
    2376            6 :                                 this->name);
    2377            6 :             SetupOutputVariable(state,
    2378              :                                 "Heat Pump Heat Recovery Operation Status",
    2379              :                                 Constant::Units::None,
    2380            6 :                                 this->heatRecoveryOperatingStatus,
    2381              :                                 OutputProcessor::TimeStepType::System,
    2382              :                                 OutputProcessor::StoreType::Average,
    2383            6 :                                 this->name);
    2384              :         }
    2385              : 
    2386              :         // find this component on the plant
    2387           35 :         bool thisErrFlag = false;
    2388          105 :         PlantUtilities::ScanPlantLoopsForObject(
    2389           70 :             state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
    2390              : 
    2391           35 :         if (thisErrFlag) {
    2392            4 :             ShowSevereError(state,
    2393            4 :                             format("{}: Plant topology problem for {} name = \"{}\"",
    2394              :                                    routineName,
    2395            2 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2396            2 :                                    this->name));
    2397            4 :             ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
    2398            2 :             errFlag = true;
    2399           33 :         } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
    2400            2 :             ShowSevereError(state,
    2401            2 :                             format("{}: Invalid connections for {} name = \"{}\"",
    2402              :                                    routineName,
    2403            1 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2404            1 :                                    this->name));
    2405            2 :             ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
    2406            1 :             errFlag = true;
    2407              :         }
    2408              : 
    2409           35 :         thisErrFlag = false;
    2410           35 :         if (this->waterSource) {
    2411           57 :             PlantUtilities::ScanPlantLoopsForObject(
    2412           38 :                 state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
    2413              : 
    2414           19 :             if (thisErrFlag) {
    2415            4 :                 ShowSevereError(state,
    2416            4 :                                 format("{}: Plant topology problem for {} name = \"{}\"",
    2417              :                                        routineName,
    2418            2 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2419            2 :                                        this->name));
    2420            4 :                 ShowContinueError(state, "Could not locate component's source side connections on a plant loop");
    2421            2 :                 errFlag = true;
    2422           17 :             } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
    2423            2 :                 ShowSevereError(state,
    2424            2 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    2425              :                                        routineName,
    2426            1 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2427            1 :                                        this->name));
    2428            2 :                 ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop");
    2429            1 :                 errFlag = true;
    2430              :             }
    2431              : 
    2432              :             // make sure it is not the same loop on both sides.
    2433           19 :             if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
    2434           10 :                 ShowSevereError(state,
    2435           10 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    2436              :                                        routineName,
    2437            5 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2438            5 :                                        this->name));
    2439           10 :                 ShowContinueError(state, "The load and source sides need to be on different loops.");
    2440            5 :                 errFlag = true;
    2441              :             } else {
    2442              : 
    2443           14 :                 PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
    2444              :             }
    2445           16 :         } else if (this->airSource) {
    2446              :             // nothing to do here ? not any more
    2447           16 :             if (this->heatRecoveryAvailable) {
    2448           18 :                 PlantUtilities::ScanPlantLoopsForObject(
    2449           12 :                     state, this->name, this->EIRHPType, this->heatRecoveryPlantLoc, thisErrFlag, _, _, _, this->heatRecoveryNodes.inlet, _);
    2450              : 
    2451            6 :                 if (thisErrFlag) {
    2452            0 :                     ShowSevereError(state,
    2453            0 :                                     format("{}: Plant topology problem for {} name = \"{}\"",
    2454              :                                            routineName,
    2455            0 :                                            DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2456            0 :                                            this->name));
    2457            0 :                     ShowContinueError(state, "Could not locate component's heat recovery side connections on a plant loop.");
    2458            0 :                     errFlag = true;
    2459            6 :                 } else if (this->heatRecoveryPlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
    2460            0 :                     ShowSevereError(state,
    2461            0 :                                     format("{}: Invalid connections for {} name = \"{}\"",
    2462              :                                            routineName,
    2463            0 :                                            DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2464            0 :                                            this->name));
    2465            0 :                     ShowContinueError(state, "The heat recovery side connections are not on the Demand Side of a plant loop.");
    2466            0 :                     errFlag = true;
    2467              :                 }
    2468              : 
    2469              :                 // make sure it is not the same loop on both sides.
    2470            6 :                 if (this->loadSidePlantLoc.loopNum == this->heatRecoveryPlantLoc.loopNum) { // user is being too tricky, don't allow
    2471            0 :                     ShowSevereError(state,
    2472            0 :                                     format("{}: Invalid connections for {} name = \"{}\"",
    2473              :                                            routineName,
    2474            0 :                                            DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2475            0 :                                            this->name));
    2476            0 :                     ShowContinueError(state, "The load and heat recovery sides need to be on different loops.");
    2477            0 :                     errFlag = true;
    2478              :                 } else {
    2479              : 
    2480            6 :                     PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->heatRecoveryPlantLoc, this->EIRHPType, true);
    2481              :                 }
    2482              :             }
    2483              :         }
    2484              : 
    2485           35 :         if (errFlag) {
    2486           10 :             ShowFatalError(state, format("{}: Program terminated due to previous condition(s).", routineName));
    2487              :         }
    2488           30 :         this->oneTimeInitFlag = false;
    2489              :     }
    2490           31 : }
    2491              : 
    2492           21 : bool EIRPlantLoopHeatPump::thermosiphonDisabled(EnergyPlusData &state)
    2493              : {
    2494           21 :     if (this->thermosiphonTempCurveIndex > 0) {
    2495            3 :         this->thermosiphonStatus = 0;
    2496            3 :         Real64 dT = this->loadSideOutletTemp - this->sourceSideInletTemp;
    2497            3 :         if (dT < this->thermosiphonMinTempDiff) {
    2498            1 :             return true;
    2499              :         }
    2500            2 :         Real64 thermosiphonCapFrac = Curve::CurveValue(state, this->thermosiphonTempCurveIndex, dT);
    2501            2 :         Real64 capFrac = this->partLoadRatio * this->cyclingRatio;
    2502            2 :         if (thermosiphonCapFrac >= capFrac) {
    2503            1 :             this->thermosiphonStatus = 1;
    2504            1 :             this->powerUsage = 0.0;
    2505            1 :             return false;
    2506              :         }
    2507            1 :         return true;
    2508              :     } else {
    2509           18 :         return true;
    2510              :     }
    2511              : }
    2512              : 
    2513            0 : Real64 EIRPlantLoopHeatPump::getDynamicMaxCapacity(EnergyPlusData &state)
    2514              : {
    2515            0 :     Real64 sourceInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
    2516            0 :     Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
    2517              :     // evaluate capacity modifier curve and determine load side heat transfer
    2518            0 :     Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, sourceInletTemp);
    2519            0 :     return this->referenceCapacity * capacityModifierFuncTemp * heatingCapacityModifierASHP(state);
    2520              : }
    2521              : 
    2522           26 : void EIRPlantLoopHeatPump::report(EnergyPlusData &state)
    2523              : {
    2524              : 
    2525           26 :     Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
    2526              : 
    2527           26 :     this->defrostEnergy = this->defrostEnergyRate * reportingInterval;
    2528           26 :     this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
    2529           26 :     this->powerEnergy = this->powerUsage * reportingInterval;
    2530           26 :     this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
    2531           26 :     this->heatRecoveryEnergy = this->heatRecoveryRate * reportingInterval;
    2532              : 
    2533              :     // update nodes
    2534           26 :     PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
    2535           26 :     state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
    2536           26 :     if (this->waterSource) {
    2537           10 :         PlantUtilities::SafeCopyPlantNode(state, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
    2538              :     }
    2539           26 :     state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
    2540           26 :     if (this->heatRecoveryAvailable) {
    2541            6 :         PlantUtilities::SafeCopyPlantNode(state, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet);
    2542            6 :         state.dataLoopNodes->Node(this->heatRecoveryNodes.outlet).Temp = this->heatRecoveryOutletTemp;
    2543              :     }
    2544           26 : }
    2545              : 
    2546              : // From here on, the Fuel Fired Heat Pump module EIRFuelFiredHeatPump
    2547              : // Enum string definitions
    2548              : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::OATempCurveVar::Num)> OATempCurveVarNamesUC = {"DRYBULB",
    2549              :                                                                                                                                     "WETBULB"};
    2550              : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::WaterTempCurveVar::Num)> WaterTempCurveVarNamesUC = {
    2551              :     "ENTERINGCONDENSER", "LEAVINGCONDENSER", "ENTERINGEVAPORATOR", "LEAVINGEVAPORATOR"};
    2552              : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::DefrostType::Num)> DefrostTypeNamesUC = {"TIMED", "ONDEMAND"};
    2553              : 
    2554           16 : void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
    2555              : {
    2556           16 :     Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
    2557              : 
    2558              :     // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
    2559              :     // I'm not sure we can count on that so we will do one check here to make sure we don't calculate things badly
    2560           16 :     if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling && currentLoad >= 0.0) ||
    2561           16 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) {
    2562            4 :         this->resetReportingVariables();
    2563            4 :         return;
    2564              :     }
    2565              : 
    2566              :     // get setpoint on the load side outlet
    2567              :     // Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
    2568              : 
    2569              :     // Use a logic similar to that for a boilder: If the specified load is 0.0 or the boiler should not run
    2570              :     // then we leave this subroutine. Before leaving
    2571              :     // if the component control is SERIESACTIVE we set the component flow to inlet flow so that flow resolver
    2572              :     // will not shut down the branch
    2573           12 :     auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
    2574           12 :     auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
    2575           12 :     auto &thisSourceSideInletNode = state.dataLoopNodes->Node(this->sourceSideNodes.inlet); // OA Intake node
    2576           12 :     auto &sim_component = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc);
    2577           12 :     if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) {
    2578            0 :         if (sim_component.FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
    2579            0 :             this->loadSideMassFlowRate = thisInletNode.MassFlowRate;
    2580              :         }
    2581            0 :         this->resetReportingVariables();
    2582            0 :         return;
    2583              :     }
    2584              : 
    2585           12 :     Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, thisInletNode.Temp, "PLFFHPEIR::simulate()");
    2586              : 
    2587              :     // Set the current load equal to the FFHP load
    2588           12 :     Real64 FFHPloadSideLoad = currentLoad; // this->loadSidePlantLoad = MyLoad;
    2589              : 
    2590              :     // Determine which air variable to use for GAHP:
    2591              :     // Source (air) side variable to use
    2592              :     // auto &thisloadsideinletnode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
    2593           12 :     Real64 oaTempforCurve = this->sourceSideInletTemp; // state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
    2594           12 :     if (this->oaTempCurveInputVar == OATempCurveVar::WetBulb) {
    2595            2 :         oaTempforCurve = Psychrometrics::PsyTwbFnTdbWPb(
    2596              :             state, thisSourceSideInletNode.Temp, thisSourceSideInletNode.HumRat, thisSourceSideInletNode.Press, "PLFFHPEIR::doPhysics()");
    2597              :     }
    2598              : 
    2599              :     // Load (water) side temperature variable
    2600           12 :     Real64 waterTempforCurve = this->loadSideInletTemp;
    2601           12 :     if (this->waterTempCurveInputVar == WaterTempCurveVar::LeavingCondenser || this->waterTempCurveInputVar == WaterTempCurveVar::LeavingEvaporator) {
    2602            2 :         waterTempforCurve = this->loadSideOutletTemp;
    2603              :     }
    2604              : 
    2605              :     // evaluate capacity modifier curve and determine load side heat transfer
    2606           12 :     Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve);
    2607              : 
    2608           12 :     if (capacityModifierFuncTemp < 0.0) {
    2609            0 :         if (this->capModFTErrorIndex == 0) {
    2610            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2611            0 :             ShowContinueError(state,
    2612            0 :                               format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
    2613            0 :             ShowContinueError(state,
    2614            0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
    2615              :                                      waterTempforCurve,
    2616              :                                      oaTempforCurve));
    2617            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
    2618              :         }
    2619            0 :         ShowRecurringWarningErrorAtEnd(state,
    2620            0 :                                        format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
    2621            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2622            0 :                                               this->name),
    2623            0 :                                        this->capModFTErrorIndex,
    2624              :                                        capacityModifierFuncTemp,
    2625              :                                        capacityModifierFuncTemp);
    2626            0 :         capacityModifierFuncTemp = 0.0;
    2627              :     }
    2628              : 
    2629           12 :     Real64 availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
    2630           12 :     Real64 partLoadRatio = 0.0;
    2631           12 :     if (availableCapacity > 0) {
    2632           24 :         partLoadRatio = std::clamp(
    2633           12 :             std::abs(FFHPloadSideLoad) / availableCapacity, 0.0, 1.0); // max(0.0, min(std::abs(FFHPloadSideLoad) / availableCapacity, 1.0));
    2634              :     }
    2635              : 
    2636              :     // evaluate the actual current operating load side heat transfer rate
    2637              : 
    2638              :     // this->loadSideHeatTransfer = availableCapacity * partLoadRatio;
    2639           12 :     this->loadSideHeatTransfer = availableCapacity * partLoadRatio; // (partLoadRatio >= this->minPLR ? partLoadRatio : 0.0);
    2640              : 
    2641              :     // calculate load side outlet conditions
    2642           12 :     Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
    2643           12 :     this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
    2644              : 
    2645              :     // calculate power usage from EIR curves
    2646           12 :     Real64 eirModifierFuncTemp = Curve::CurveValue(state,
    2647              :                                                    this->powerRatioFuncTempCurveIndex,
    2648              :                                                    waterTempforCurve,
    2649           12 :                                                    oaTempforCurve); // CurveManager::CurveValue(state, this->powerRatioFuncTempCurveIndex,
    2650              :                                                                     // this->loadSideOutletTemp, this->sourceSideInletTemp);
    2651              : 
    2652           12 :     if (eirModifierFuncTemp < 0.0) {
    2653            0 :         if (this->eirModFTErrorIndex == 0) {
    2654            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2655            0 :             ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
    2656            0 :             ShowContinueError(state,
    2657            0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
    2658              :                                      waterTempforCurve,
    2659              :                                      oaTempforCurve));
    2660            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
    2661              :         }
    2662            0 :         ShowRecurringWarningErrorAtEnd(state,
    2663            0 :                                        format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
    2664            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2665            0 :                                               this->name),
    2666            0 :                                        this->eirModFTErrorIndex,
    2667              :                                        eirModifierFuncTemp,
    2668              :                                        eirModifierFuncTemp);
    2669            0 :         eirModifierFuncTemp = 0.0;
    2670              :     }
    2671              : 
    2672           12 :     Real64 miniPLR_mod = this->minPLR;
    2673           12 :     Real64 PLFf = max(miniPLR_mod, partLoadRatio);
    2674              : 
    2675           12 :     Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, PLFf);
    2676              :     // this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp;
    2677              :     // this->powerEnergy = this->powerUsage * reportingInterval;
    2678              : 
    2679           12 :     if (eirModifierFuncPLR < 0.0) {
    2680            0 :         if (this->eirModFPLRErrorIndex == 0) {
    2681            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2682            0 :             ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
    2683            0 :             ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", PLFf));
    2684            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
    2685              :         }
    2686            0 :         ShowRecurringWarningErrorAtEnd(state,
    2687            0 :                                        format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
    2688            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2689            0 :                                               this->name),
    2690            0 :                                        this->eirModFPLRErrorIndex,
    2691              :                                        eirModifierFuncPLR,
    2692              :                                        eirModifierFuncPLR);
    2693            0 :         eirModifierFuncPLR = 0.0;
    2694              :     }
    2695              : 
    2696           12 :     constexpr Real64 minDefrostT = Fahrenheit2Celsius(16.0); // (16.0 - 32.0) * 5.0 / 9.0; // 16F
    2697           12 :     constexpr Real64 maxDefrostT = Fahrenheit2Celsius(38.0); // (38.0 - 32.0) * 5.0 / 9.0; // 38F
    2698              : 
    2699           12 :     Real64 oaTemp2 = std::clamp(oaTempforCurve, minDefrostT, maxDefrostT); // max(minDefrostT, min(maxDefrostT, oaTempforCurve));
    2700           12 :     Real64 eirDefrost = 1.0;
    2701              : 
    2702           12 :     if ((state.dataEnvrn->OutDryBulbTemp <= this->defrostMaxOADBT) && this->defrostType == DefrostType::OnDemand) {
    2703           10 :         if (this->defrostEIRCurveIndex > 0) {
    2704            4 :             eirDefrost = Curve::CurveValue(state, this->defrostEIRCurveIndex, oaTemp2);
    2705              :         }
    2706              : 
    2707           10 :         if (eirDefrost < 1.0) {
    2708            0 :             if (this->eirDefrostFTErrorIndex == 0) {
    2709            0 :                 ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2710            0 :                 ShowContinueError(state,
    2711            0 :                                   format(" EIR defrost Modifier curve (function of Temperature) output is less than 1.0 ({:.3T}).", eirDefrost));
    2712            0 :                 ShowContinueError(state, format(" Negative value occurs using an outdoor air temperature of {:.2T}", oaTemp2));
    2713            0 :                 ShowContinueErrorTimeStamp(state, " Resetting curve output to 1.0 and continuing simulation.");
    2714              :             }
    2715            0 :             ShowRecurringWarningErrorAtEnd(state,
    2716            0 :                                            format("{} \"{}\": EIR Modifier curve (function of PLR) output out of range warning continues...",
    2717            0 :                                                   DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2718            0 :                                                   this->name),
    2719            0 :                                            this->eirDefrostFTErrorIndex,
    2720              :                                            eirDefrost,
    2721              :                                            eirDefrost);
    2722            0 :             eirDefrost = 1.0;
    2723              :         }
    2724              :     }
    2725              : 
    2726              :     // Cycling degradation calculation
    2727           12 :     Real64 CRF = 1.0;
    2728           12 :     constexpr Real64 CRF_Slope =
    2729              :         0.4167; // default curve coefficients from "Pathways to Decarbonization of Residential Heating", Fridlyand et al. (2021)
    2730           12 :     constexpr Real64 CRF_Intercept = 0.5833;
    2731           12 :     if (partLoadRatio < this->minimumUnloadingRatio) {
    2732            5 :         Real64 CR = std::clamp(partLoadRatio / this->minimumUnloadingRatio, 0.0, 1.0);
    2733            5 :         if (this->cycRatioCurveIndex > 0) {
    2734            3 :             CRF = Curve::CurveValue(state, this->cycRatioCurveIndex, CR);
    2735              :         } else {
    2736            2 :             CRF = CRF_Slope * CR + CRF_Intercept;
    2737              :         }
    2738              :     }
    2739           12 :     if (CRF <= Constant::rTinyValue) {
    2740            0 :         CRF = CRF_Intercept;
    2741              :     }
    2742           12 :     this->cyclingRatioFraction = CRF;
    2743              : 
    2744              :     // aux elec
    2745           12 :     Real64 eirAuxElecFuncTemp = 0.0;
    2746           12 :     if (this->auxElecEIRFoTempCurveIndex > 0) {
    2747           12 :         eirAuxElecFuncTemp = Curve::CurveValue(state, this->auxElecEIRFoTempCurveIndex, waterTempforCurve, oaTempforCurve);
    2748              :     }
    2749              : 
    2750           12 :     if (eirAuxElecFuncTemp < 0.0) {
    2751            0 :         if (this->eirAuxElecFTErrorIndex == 0) {
    2752            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2753            0 :             ShowContinueError(state,
    2754            0 :                               format(" Auxillary EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirAuxElecFuncTemp));
    2755            0 :             ShowContinueError(state,
    2756            0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
    2757              :                                      waterTempforCurve,
    2758              :                                      oaTempforCurve));
    2759            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
    2760              :         }
    2761            0 :         ShowRecurringWarningErrorAtEnd(
    2762              :             state,
    2763            0 :             format("{} \"{}\": Auxillary EIR Modifier curve (function of Temperatures) output is negative warning continues...",
    2764            0 :                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2765            0 :                    this->name),
    2766            0 :             this->eirAuxElecFTErrorIndex,
    2767              :             eirAuxElecFuncTemp,
    2768              :             eirAuxElecFuncTemp);
    2769            0 :         eirAuxElecFuncTemp = 0.0;
    2770              :     }
    2771              : 
    2772           12 :     Real64 eirAuxElecFuncPLR = 0.0;
    2773           12 :     if (this->auxElecEIRFoPLRCurveIndex > 0) {
    2774           12 :         eirAuxElecFuncPLR = Curve::CurveValue(state, this->auxElecEIRFoPLRCurveIndex, partLoadRatio);
    2775              :     }
    2776              : 
    2777           12 :     if (eirAuxElecFuncPLR < 0.0) {
    2778            0 :         if (this->eirAuxElecFPLRErrorIndex == 0) {
    2779            0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
    2780            0 :             ShowContinueError(state,
    2781            0 :                               format(" Auxillary EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirAuxElecFuncPLR));
    2782            0 :             ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}.", partLoadRatio));
    2783            0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
    2784              :         }
    2785            0 :         ShowRecurringWarningErrorAtEnd(state,
    2786            0 :                                        format("{} \"{}\": Auxillary EIR Modifier curve (function of PLR) output is negative warning continues...",
    2787            0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    2788            0 :                                               this->name),
    2789            0 :                                        this->eirAuxElecFPLRErrorIndex,
    2790              :                                        eirAuxElecFuncPLR,
    2791              :                                        eirAuxElecFuncPLR);
    2792            0 :         eirAuxElecFuncPLR = 0.0;
    2793              :     }
    2794              : 
    2795           12 :     this->fuelRate = this->loadSideHeatTransfer / (this->referenceCOP * CRF) * eirModifierFuncPLR * eirModifierFuncTemp * eirDefrost;
    2796              : 
    2797           12 :     this->powerUsage = this->nominalAuxElecPower * eirAuxElecFuncTemp * eirAuxElecFuncPLR;
    2798           12 :     if (this->defrostType == DefrostType::Timed) {
    2799            0 :         this->powerUsage += this->defrostResistiveHeaterCap * this->defrostOpTimeFrac;
    2800              :     }
    2801              : 
    2802           12 :     this->powerUsage += this->standbyElecPower;
    2803              : 
    2804              :     // energy balance on heat pump
    2805              :     // this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
    2806           12 :     this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->fuelRate + this->powerUsage - this->standbyElecPower);
    2807              : 
    2808              :     // calculate source side outlet conditions
    2809           12 :     Real64 CpSrc = 0.0;
    2810           12 :     if (this->waterSource) {
    2811            0 :         auto &thisSourcePlantLoop = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum);
    2812            0 :         CpSrc = thisSourcePlantLoop.glycol->getSpecificHeat(state, this->sourceSideInletTemp, "PLFFHPEIR::simulate()");
    2813           12 :     } else if (this->airSource) {
    2814           12 :         CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
    2815              :     }
    2816              :     // this->sourceSideCp = CpSrc; // debuging variable
    2817              :     // Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
    2818           12 :     Real64 const sourceMCp = (this->sourceSideMassFlowRate < 1e-6 ? 1.0 : this->sourceSideMassFlowRate) * CpSrc;
    2819           12 :     this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
    2820              : }
    2821              : 
    2822            0 : void EIRFuelFiredHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
    2823              : {
    2824              :     // size the source-side for the air-source HP
    2825            0 :     bool errorsFound = false;
    2826              : 
    2827              :     // these variables will be used throughout this function as a temporary value of that physical state
    2828            0 :     Real64 tmpCapacity = this->referenceCapacity;
    2829            0 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
    2830            0 :     Real64 tmpSourceVolFlow = 0.0;
    2831              : 
    2832              :     // will leave like this for now
    2833              :     // need to update these to better values later
    2834            0 :     Real64 sourceSideInitTemp = 20.0;
    2835            0 :     Real64 sourceSideHumRat = 0.0;
    2836            0 :     if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
    2837              :         // same here; update later
    2838            0 :         sourceSideInitTemp = 20.0;
    2839              :     }
    2840              : 
    2841            0 :     Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
    2842            0 :     Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
    2843              : 
    2844              :     // set the source-side flow rate
    2845            0 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
    2846              :         // load-side capacity should already be set, so unless the flow rate is specified, we can set
    2847              :         // an assumed reasonable flow rate since this doesn't affect downstream components
    2848            0 :         Real64 DeltaT_src = 10.0;
    2849              :         // to get the source flow, we first must calculate the required heat impact on the source side
    2850              :         // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
    2851              :         // Then the energy balance:     Qsrc = Qload + Power
    2852              :         // Substituting for Power:      Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
    2853            0 :         Real64 const designSourceSideHeatTransfer = tmpCapacity * (1.0 + 1.0 / this->referenceCOP);
    2854              :         // To get the design source flow rate, just apply the sensible heat rate equation:
    2855              :         //                              Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
    2856              :         //                              Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
    2857            0 :         tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
    2858            0 :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0.0) {
    2859              :         // given the value by the user
    2860              :         // set it directly
    2861            0 :         tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
    2862              :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0.0) { // LCOV_EXCL_LINE
    2863              :         // user gave a flow rate of 0
    2864              :         // protected by the input processor to be >0.0
    2865              :         // fatal out just in case
    2866              :         errorsFound = true; // LCOV_EXCL_LINE
    2867            0 :         ShowSevereError(state,
    2868            0 :                         format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
    2869            0 :                                this->name,
    2870              :                                this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
    2871              :     } else {
    2872              :         // can't imagine how it would ever get to this point
    2873              :         // just assume it's the same as the load side if we don't have any sizing information
    2874              :         tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
    2875              :     }
    2876              : 
    2877            0 :     this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
    2878              : 
    2879            0 :     if (errorsFound) {
    2880              :         ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
    2881              :     }
    2882            0 : }
    2883              : 
    2884            6 : void EIRFuelFiredHeatPump::resetReportingVariables()
    2885              : {
    2886            6 :     this->loadSideHeatTransfer = 0.0;
    2887            6 :     this->loadSideEnergy = 0.0;
    2888            6 :     this->loadSideOutletTemp = this->loadSideInletTemp;
    2889            6 :     this->fuelRate = 0.0;
    2890            6 :     this->fuelEnergy = 0.0;
    2891            6 :     this->powerUsage = 0.0;
    2892            6 :     this->powerEnergy = 0.0;
    2893            6 :     this->sourceSideHeatTransfer = 0.0;
    2894            6 :     this->sourceSideOutletTemp = this->sourceSideInletTemp;
    2895            6 :     this->sourceSideEnergy = 0.0;
    2896            6 : }
    2897              : 
    2898           11 : PlantComponent *EIRFuelFiredHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type, const std::string &hp_name)
    2899              : {
    2900           11 :     if (state.dataEIRFuelFiredHeatPump->getInputsFFHP) {
    2901            6 :         EIRFuelFiredHeatPump::processInputForEIRPLHP(state);
    2902            6 :         EIRFuelFiredHeatPump::pairUpCompanionCoils(state);
    2903            6 :         state.dataEIRFuelFiredHeatPump->getInputsFFHP = false;
    2904              :     }
    2905              : 
    2906           16 :     for (auto &plhp : state.dataEIRFuelFiredHeatPump->heatPumps) {
    2907           12 :         if (plhp.name == Util::makeUPPER(hp_name) && plhp.EIRHPType == hp_type) {
    2908            7 :             return &plhp;
    2909              :         }
    2910           18 :     }
    2911              : 
    2912            8 :     ShowFatalError(state, format("EIR Fuel-Fired Heat Pump factory: Error getting inputs for PLFFHP named: {}.", hp_name));
    2913              :     return nullptr; // LCOV_EXCL_LINE
    2914              : }
    2915              : 
    2916            6 : void EIRFuelFiredHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
    2917              : {
    2918           13 :     for (auto &thisHP : state.dataEIRFuelFiredHeatPump->heatPumps) {
    2919            7 :         if (!thisHP.companionCoilName.empty()) {
    2920            2 :             std::string thisCoilName = Util::makeUPPER(thisHP.name);
    2921            2 :             DataPlant::PlantEquipmentType thisCoilType = thisHP.EIRHPType;
    2922            2 :             std::string targetCompanionName = Util::makeUPPER(thisHP.companionCoilName);
    2923            3 :             for (auto &potentialCompanionCoil : state.dataEIRFuelFiredHeatPump->heatPumps) {
    2924            3 :                 DataPlant::PlantEquipmentType potentialCompanionType = potentialCompanionCoil.EIRHPType;
    2925            3 :                 std::string potentialCompanionName = Util::makeUPPER(potentialCompanionCoil.name);
    2926            3 :                 if (potentialCompanionName == thisCoilName) {
    2927              :                     // skip the current coil
    2928            1 :                     continue;
    2929              :                 }
    2930            2 :                 if (potentialCompanionName == targetCompanionName) {
    2931            2 :                     if (thisCoilType == potentialCompanionType) {
    2932            0 :                         ShowSevereError(state,
    2933            0 :                                         format("Invalid companion specification for EIR Plant Loop Fuel-Fired Heat Pump named \"{}\"", thisCoilName));
    2934            0 :                         ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
    2935            0 :                         ShowFatalError(state, "Invalid companion object causes program termination");
    2936              :                     }
    2937            2 :                     thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
    2938            2 :                     break;
    2939              :                 }
    2940            5 :             }
    2941            2 :             if (!thisHP.companionHeatPumpCoil) {
    2942            0 :                 ShowSevereError(state, "Could not find matching companion heat pump coil.");
    2943            0 :                 ShowContinueError(state, format("Base coil: {}", thisCoilName));
    2944            0 :                 ShowContinueError(state, format("Looking for companion coil named: {}", targetCompanionName));
    2945            0 :                 ShowFatalError(state, "Simulation aborts due to previous severe error");
    2946              :             }
    2947            2 :         }
    2948            6 :     }
    2949            6 : }
    2950              : 
    2951            6 : void EIRFuelFiredHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
    2952              : {
    2953              :     struct ClassType
    2954              :     {
    2955              :         DataPlant::PlantEquipmentType thisType;
    2956              :         std::string nodesType;
    2957              :         std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
    2958              :         std::function<Real64(Real64, Real64)> calcQsource;
    2959              :         std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
    2960              : 
    2961           12 :         ClassType(DataPlant::PlantEquipmentType _thisType,
    2962              :                   std::string _nodesType,
    2963              :                   std::function<Real64(Real64, Real64)> _tLoadOutFunc,
    2964              :                   std::function<Real64(Real64, Real64)> _qSrcFunc,
    2965              :                   std::function<Real64(Real64, Real64)> _tSrcOutFunc)
    2966           12 :             : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(_tLoadOutFunc), calcQsource(_qSrcFunc),
    2967           12 :               calcSourceOutletTemp(_tSrcOutFunc)
    2968              :         {
    2969           12 :         }
    2970              :     };
    2971              :     std::array<ClassType, 2> classesToInput = {
    2972              :         ClassType{DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling,
    2973              :                   "Chilled Water Nodes",
    2974              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract,
    2975              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add,
    2976              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add},
    2977              :         ClassType{DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating,
    2978              :                   "Hot Water Nodes",
    2979              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add,
    2980              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract,
    2981              :                   EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract},
    2982           12 :     };
    2983              : 
    2984            6 :     bool errorsFound = false;
    2985            6 :     std::string &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
    2986           18 :     for (auto &classToInput : classesToInput) {
    2987           12 :         cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
    2988              : 
    2989              :         DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
    2990           12 :             getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(cCurrentModuleObject)));
    2991              : 
    2992           12 :         auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
    2993           12 :         if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
    2994            5 :             continue;
    2995              :         }
    2996            7 :         auto &instancesValue = instances.value();
    2997           14 :         for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
    2998            7 :             auto const &fields = instance.value();
    2999            7 :             auto const &thisObjectName = instance.key();
    3000            7 :             state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
    3001              : 
    3002            7 :             EIRFuelFiredHeatPump thisPLHP;
    3003              : 
    3004            7 :             thisPLHP.EIRHPType = classToInput.thisType;
    3005           14 :             std::string companionCoilFieldTag = "companion_heating_heat_pump_name";
    3006            7 :             std::string refCapFieldTag = "nominal_cooling_capacity";
    3007            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) {
    3008            6 :                 companionCoilFieldTag = "companion_cooling_heat_pump_name";
    3009            6 :                 refCapFieldTag = "nominal_heating_capacity";
    3010              :             }
    3011              : 
    3012              :             // A1-A3
    3013            7 :             thisPLHP.name = Util::makeUPPER(thisObjectName);
    3014           14 :             std::string loadSideInletNodeName = Util::makeUPPER(fields.at("water_inlet_node_name").get<std::string>());
    3015            7 :             std::string loadSideOutletNodeName = Util::makeUPPER(fields.at("water_outlet_node_name").get<std::string>());
    3016              :             // Implicit
    3017              :             // std::string condenserType = "AIRSOURCE"; // Util::makeUPPER(fields.at("condenser_type").get<std::string>());
    3018            7 :             thisPLHP.airSource = true;
    3019            7 :             thisPLHP.waterSource = false;
    3020              : 
    3021              :             // A4
    3022            7 :             std::string sourceSideInletNodeName = Util::makeUPPER(fields.at("air_source_node_name").get<std::string>());
    3023              :             // Util::makeUPPER(fields.at("source_side_outlet_node_name").get<std::string>());
    3024            7 :             std::string sourceSideOutletNodeName = format("{}_SOURCE_SIDE_OUTLET_NODE", thisPLHP.name);
    3025              : 
    3026              :             // A5
    3027            7 :             auto compCoilFound = fields.find(companionCoilFieldTag);
    3028            7 :             if (compCoilFound != fields.end()) { // optional field
    3029            2 :                 thisPLHP.companionCoilName = Util::makeUPPER(compCoilFound.value().get<std::string>());
    3030              :             }
    3031              : 
    3032              :             // A6 Fuel Type
    3033            7 :             std::string tempRsrStr = Util::makeUPPER(fields.at("fuel_type").get<std::string>());
    3034            7 :             thisPLHP.fuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, tempRsrStr));
    3035              :             // Validate fuel type input
    3036              :             static constexpr std::string_view RoutineName("processInputForEIRPLHP: ");
    3037            7 :             if (thisPLHP.fuelType == Constant::eFuel::Invalid) {
    3038            0 :                 ShowSevereError(state, format("{}{}=\"{}\",", RoutineName, cCurrentModuleObject, thisPLHP.name));
    3039            0 :                 ShowContinueError(state, format("Invalid Fuel Type = {}", tempRsrStr));
    3040            0 :                 ShowContinueError(state, "Reset the Fuel Type to \"NaturalGas\".");
    3041            0 :                 thisPLHP.fuelType = Constant::eFuel::NaturalGas;
    3042            0 :                 errorsFound = true;
    3043              :             }
    3044              : 
    3045              :             // A7 End use category
    3046            7 :             thisPLHP.endUseSubcat = Util::makeUPPER(fields.at("end_use_subcategory").get<std::string>());
    3047            7 :             if (thisPLHP.endUseSubcat == "") {
    3048            0 :                 thisPLHP.endUseSubcat = "Heat Pump Fuel Fired"; // or "General"?
    3049              :             }
    3050              : 
    3051              :             // N1 Nominal heating capacity
    3052            7 :             auto &tmpRefCapacity = fields.at(refCapFieldTag);
    3053              : 
    3054            7 :             if (tmpRefCapacity == "Autosize") {
    3055            0 :                 thisPLHP.referenceCapacity = DataSizing::AutoSize;
    3056            0 :                 thisPLHP.referenceCapacityWasAutoSized = true;
    3057              :             } else {
    3058            7 :                 thisPLHP.referenceCapacity = tmpRefCapacity.get<Real64>();
    3059              :             }
    3060              : 
    3061              :             // N2 Nominal heating capacity
    3062            7 :             thisPLHP.referenceCOP = fields.at("nominal_cop").get<Real64>();
    3063            7 :             if (thisPLHP.referenceCOP <= 0.0) {
    3064            0 :                 thisPLHP.referenceCOP = 1.0;
    3065              :             }
    3066              : 
    3067              :             // N3 Design flow rate
    3068            7 :             auto &tmpFlowRate = fields.at("design_flow_rate");
    3069            7 :             if (tmpFlowRate == "Autosize") {
    3070            0 :                 thisPLHP.loadSideDesignVolFlowRate = DataSizing::AutoSize;
    3071            0 :                 thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
    3072              :             } else {
    3073            7 :                 thisPLHP.loadSideDesignVolFlowRate = tmpFlowRate.get<Real64>();
    3074              :             }
    3075              : 
    3076              :             // GAHP: Add a default source side flow rate, not from input
    3077            7 :             Real64 defDummyASDesVolFlowRate = 1.0;
    3078            7 :             thisPLHP.sourceSideDesignVolFlowRate = defDummyASDesVolFlowRate;
    3079              : 
    3080              :             // N4 Design supply temperature
    3081            7 :             auto &tmpDesSupTemp = fields.at("design_supply_temperature");
    3082            7 :             if (tmpDesSupTemp == "Autosize") {
    3083              :                 // sizing
    3084              :             } else {
    3085            7 :                 thisPLHP.desSupplyTemp = tmpDesSupTemp.get<Real64>();
    3086              :             }
    3087              : 
    3088              :             // N5 Design temperature lift
    3089            7 :             auto &tmpDesTempLift = fields.at("design_temperature_lift");
    3090            7 :             if (tmpDesTempLift == "Autosize") {
    3091              :                 // sizing
    3092              :             } else {
    3093            7 :                 thisPLHP.desTempLift = tmpDesTempLift.get<Real64>();
    3094              :             }
    3095              : 
    3096              :             // N6 Sizing factor
    3097            7 :             auto sizeFactorFound = fields.find("sizing_factor");
    3098            7 :             if (sizeFactorFound != fields.end()) {
    3099            7 :                 thisPLHP.sizingFactor = sizeFactorFound.value().get<Real64>();
    3100              :             } else {
    3101            0 :                 Real64 defaultVal_sizeFactor = 1.0;
    3102            0 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    3103              :                         state, cCurrentModuleObject, "sizing_factor", defaultVal_sizeFactor)) {
    3104            0 :                     ShowSevereError(state, "EIR FFHP: Sizing factor not entered and could not get default value");
    3105            0 :                     errorsFound = true;
    3106              :                 } else {
    3107            0 :                     thisPLHP.sizingFactor = defaultVal_sizeFactor;
    3108              :                 }
    3109              :             }
    3110              : 
    3111              :             // A8 flow mode
    3112            7 :             thisPLHP.flowMode = static_cast<DataPlant::FlowMode>(
    3113           14 :                 getEnumValue(DataPlant::FlowModeNamesUC, Util::makeUPPER(fields.at("flow_mode").get<std::string>())));
    3114              : 
    3115              :             // A9 outdoor_air_temperature_curve_input_variable
    3116            7 :             std::string oaTempCurveInputVar = Util::makeUPPER(fields.at("outdoor_air_temperature_curve_input_variable").get<std::string>());
    3117            7 :             thisPLHP.oaTempCurveInputVar = static_cast<OATempCurveVar>(getEnumValue(OATempCurveVarNamesUC, oaTempCurveInputVar));
    3118              : 
    3119              :             // A10 water_temperature_curve_input_variable
    3120            7 :             std::string waterTempCurveInputVar = Util::makeUPPER(fields.at("water_temperature_curve_input_variable").get<std::string>());
    3121            7 :             thisPLHP.waterTempCurveInputVar = static_cast<WaterTempCurveVar>(getEnumValue(WaterTempCurveVarNamesUC, waterTempCurveInputVar));
    3122              : 
    3123              :             // A11 normalized_capacity_function_of_temperature_curve_name
    3124            7 :             std::string const &capFtName = Util::makeUPPER(fields.at("normalized_capacity_function_of_temperature_curve_name").get<std::string>());
    3125              : 
    3126            7 :             thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, capFtName);
    3127            7 :             if (thisPLHP.capFuncTempCurveIndex == 0) {
    3128            0 :                 ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, capFtName));
    3129            0 :                 errorsFound = true;
    3130              :             }
    3131              : 
    3132              :             // A12 fuel_energy_input_ratio_function_of_temperature_curve_name
    3133              :             std::string const &eirFtName =
    3134            7 :                 Util::makeUPPER(fields.at("fuel_energy_input_ratio_function_of_temperature_curve_name").get<std::string>());
    3135            7 :             thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, eirFtName);
    3136            7 :             if (thisPLHP.capFuncTempCurveIndex == 0) {
    3137            0 :                 ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, eirFtName));
    3138            0 :                 errorsFound = true;
    3139              :             }
    3140              :             // A13 fuel_energy_input_ratio_function_of_plr_curve_name
    3141            7 :             std::string const &eirFplrName = Util::makeUPPER(fields.at("fuel_energy_input_ratio_function_of_plr_curve_name").get<std::string>());
    3142            7 :             thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, eirFplrName);
    3143            7 :             if (thisPLHP.capFuncTempCurveIndex == 0) {
    3144            0 :                 ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, eirFplrName));
    3145            0 :                 errorsFound = true;
    3146              :             }
    3147              : 
    3148              :             // N7 min PLR
    3149            7 :             auto minPLRFound = fields.find("minimum_part_load_ratio");
    3150            7 :             if (minPLRFound != fields.end()) {
    3151            7 :                 thisPLHP.minPLR = minPLRFound.value().get<Real64>();
    3152              :             } else {
    3153            0 :                 Real64 defaultVal = 0.1;
    3154            0 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "minimum_part_load_ratio", defaultVal)) {
    3155            0 :                     ShowSevereError(state, "EIR PLFFHP: minimum PLR not entered and could not get default value.");
    3156            0 :                     errorsFound = true;
    3157              :                 } else {
    3158            0 :                     thisPLHP.minPLR = defaultVal;
    3159              :                 }
    3160              :             }
    3161              : 
    3162              :             // N8 max PLR
    3163            7 :             auto maxPLRFound = fields.find("maximum_part_load_ratio");
    3164            7 :             if (maxPLRFound != fields.end()) {
    3165            7 :                 thisPLHP.maxPLR = maxPLRFound.value().get<Real64>();
    3166              :             } else {
    3167            0 :                 Real64 defaultVal = 1.0;
    3168            0 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "maximum_part_load_ratio", defaultVal)) {
    3169            0 :                     ShowSevereError(state, "EIR PLFFHP: maximum PLR not entered and could not get default value.");
    3170            0 :                     errorsFound = true;
    3171              :                 } else {
    3172            0 :                     thisPLHP.maxPLR = defaultVal;
    3173              :                 }
    3174              :             }
    3175              : 
    3176              :             // A14 fuel_energy_input_ratio_defrost_adjustment_curve_name
    3177            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3178            1 :                 thisPLHP.defrostEIRCurveIndex = 0;
    3179              :             } else {
    3180            6 :                 auto eirDefrostCurveFound = fields.find("fuel_energy_input_ratio_defrost_adjustment_curve_name");
    3181            6 :                 if (eirDefrostCurveFound != fields.end()) {
    3182            3 :                     std::string const eirDefrostCurveName = Util::makeUPPER(eirDefrostCurveFound.value().get<std::string>());
    3183            3 :                     thisPLHP.defrostEIRCurveIndex = Curve::GetCurveIndex(state, eirDefrostCurveName);
    3184            3 :                     if (thisPLHP.defrostEIRCurveIndex == 0) {
    3185            0 :                         ShowSevereError(
    3186            0 :                             state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, eirDefrostCurveName));
    3187            0 :                         errorsFound = true;
    3188              :                     }
    3189            3 :                 } else {
    3190            3 :                     thisPLHP.defrostEIRCurveIndex = 0;
    3191              :                 }
    3192            6 :             }
    3193              : 
    3194              :             // A15 defrost_control_type
    3195            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3196            1 :                 thisPLHP.defrostType = DefrostType::Invalid;
    3197              :             } else {
    3198            6 :                 thisPLHP.defrostType =
    3199            6 :                     static_cast<DefrostType>(getEnumValue(DefrostTypeNamesUC, Util::makeUPPER(fields.at("defrost_control_type").get<std::string>())));
    3200            6 :                 if (thisPLHP.defrostType == DefrostType::Invalid) {
    3201            0 :                     thisPLHP.defrostType = DefrostType::OnDemand; // set to default
    3202            0 :                     thisPLHP.defrostOpTimeFrac = 0.0;
    3203            0 :                     ShowWarningError(state, format("Invalid Defrost Control Type for EIR PLFFHP ({} name={})", cCurrentModuleObject, thisPLHP.name));
    3204            0 :                     ShowContinueError(state,
    3205            0 :                                       format("The Input Variable is reset to: {}", DefrostTypeNamesUC[static_cast<int>(thisPLHP.defrostType)]));
    3206              :                 }
    3207              :             }
    3208              : 
    3209              :             // N9 defrost_operation_time_fraction
    3210            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3211            1 :                 thisPLHP.defrostOpTimeFrac = 0.0;
    3212              :             } else {
    3213            6 :                 auto defrostOpTimeFracFound = fields.find("defrost_operation_time_fraction");
    3214            6 :                 if (defrostOpTimeFracFound != fields.end()) {
    3215            1 :                     thisPLHP.defrostOpTimeFrac = defrostOpTimeFracFound.value().get<Real64>();
    3216              :                 } else {
    3217            5 :                     Real64 defaultVal = 0.0;
    3218           15 :                     if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    3219              :                             state, cCurrentModuleObject, "defrost_operation_time_fraction", defaultVal)) {
    3220            0 :                         ShowSevereError(state, "EIR PLFFHP: defrost time fraction not entered and could not get default value.");
    3221            0 :                         errorsFound = true;
    3222              :                     } else {
    3223            5 :                         thisPLHP.defrostOpTimeFrac = defaultVal;
    3224              :                     }
    3225              :                 }
    3226            6 :             }
    3227              : 
    3228              :             // N10 Resistive Defrost Heater Capacity
    3229            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3230            1 :                 thisPLHP.defrostResistiveHeaterCap = 0.0;
    3231              :             } else {
    3232            6 :                 auto resDefrostHeaterCapFound = fields.find("resistive_defrost_heater_capacity");
    3233            6 :                 if (resDefrostHeaterCapFound != fields.end()) {
    3234            3 :                     thisPLHP.defrostResistiveHeaterCap = resDefrostHeaterCapFound.value().get<Real64>();
    3235              :                 } else {
    3236            3 :                     Real64 defaultVal = 0.0;
    3237            9 :                     if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    3238              :                             state, cCurrentModuleObject, "resistive_defrost_heater_capacity", defaultVal)) {
    3239            0 :                         ShowSevereError(state, "EIR PLFFHP: Resistive Defrost Heater Capacity not entered and could not get default value.");
    3240            0 :                         errorsFound = true;
    3241              :                     } else {
    3242            3 :                         thisPLHP.defrostResistiveHeaterCap = defaultVal;
    3243              :                     }
    3244              :                 }
    3245            6 :             }
    3246              : 
    3247              :             // N11 maximum_outdoor_dry_bulb_temperature_for_defrost_operation
    3248            7 :             if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3249            1 :                 thisPLHP.defrostMaxOADBT = -99.0;
    3250              :             } else {
    3251            6 :                 auto maxOADBTFound = fields.find("maximum_outdoor_dry_bulb_temperature_for_defrost_operation");
    3252            6 :                 if (maxOADBTFound != fields.end()) {
    3253            4 :                     thisPLHP.defrostMaxOADBT = maxOADBTFound.value().get<Real64>();
    3254              :                 } else {
    3255            2 :                     Real64 defaultVal = 5.0;
    3256            6 :                     if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    3257              :                             state, cCurrentModuleObject, "maximum_outdoor_dry_bulb_temperature_for_defrost_operation", defaultVal)) {
    3258            0 :                         ShowSevereError(state, "EIR PLFFHP: max defrost operation OA temperature not entered and could not get default value.");
    3259            0 :                         errorsFound = true;
    3260              :                     } else {
    3261            2 :                         thisPLHP.defrostMaxOADBT = defaultVal;
    3262              :                     }
    3263              :                 }
    3264            6 :             }
    3265              : 
    3266              :             // A16 cycling_ratio_factor_curve_name
    3267            7 :             auto crfCurveFound = fields.find("cycling_ratio_factor_curve_name");
    3268            7 :             if (crfCurveFound != fields.end()) {
    3269            7 :                 std::string const cycRatioCurveName = Util::makeUPPER(crfCurveFound.value().get<std::string>());
    3270            7 :                 thisPLHP.cycRatioCurveIndex = Curve::GetCurveIndex(state, cycRatioCurveName);
    3271            7 :                 if (thisPLHP.cycRatioCurveIndex == 0) {
    3272            0 :                     ShowSevereError(state,
    3273            0 :                                     format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {})", thisPLHP.name, cycRatioCurveName));
    3274            0 :                     errorsFound = true;
    3275              :                 }
    3276            7 :             } else {
    3277            0 :                 thisPLHP.cycRatioCurveIndex = 0;
    3278              :             }
    3279              : 
    3280              :             // N12 nominal_auxiliary_electric_power
    3281            7 :             auto nomAuxElecPowerFound = fields.find("nominal_auxiliary_electric_power");
    3282            7 :             if (nomAuxElecPowerFound != fields.end()) {
    3283            7 :                 thisPLHP.nominalAuxElecPower = nomAuxElecPowerFound.value().get<Real64>();
    3284              :             } else {
    3285            0 :                 Real64 defaultVal = 0.0;
    3286            0 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
    3287              :                         state, cCurrentModuleObject, "nominal_auxiliary_electric_power", defaultVal)) {
    3288            0 :                     ShowSevereError(state, "EIR PLFFHP: nominal auxiliary electric power not entered and could not get default value.");
    3289            0 :                     errorsFound = true;
    3290              :                 } else {
    3291            0 :                     thisPLHP.nominalAuxElecPower = defaultVal;
    3292              :                 }
    3293              :             }
    3294              : 
    3295              :             // A17 auxiliary_electric_energy_input_ratio_function_of_temperature_curve_name
    3296            7 :             auto auxElecEIRFTCurveFound = fields.find("auxiliary_electric_energy_input_ratio_function_of_temperature_curve_name");
    3297            7 :             if (auxElecEIRFTCurveFound != fields.end()) {
    3298            7 :                 std::string const &auxEIRFTName = Util::makeUPPER(auxElecEIRFTCurveFound.value().get<std::string>());
    3299            7 :                 thisPLHP.auxElecEIRFoTempCurveIndex = Curve::GetCurveIndex(state, auxEIRFTName);
    3300            7 :                 if (thisPLHP.auxElecEIRFoTempCurveIndex == 0) {
    3301            0 :                     ShowSevereError(state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, auxEIRFTName));
    3302            0 :                     errorsFound = true;
    3303              :                 }
    3304            7 :             } else {
    3305            0 :                 thisPLHP.auxElecEIRFoTempCurveIndex = 0;
    3306              :             }
    3307              : 
    3308              :             // A18 auxiliary_electric_energy_input_ratio_function_of_plr_curve_name
    3309            7 :             auto auxElecEIRFPLRCurveFound = fields.find("auxiliary_electric_energy_input_ratio_function_of_plr_curve_name");
    3310            7 :             if (auxElecEIRFPLRCurveFound != fields.end()) {
    3311            7 :                 std::string const &auxEIRFPLRName = Util::makeUPPER(auxElecEIRFPLRCurveFound.value().get<std::string>());
    3312            7 :                 thisPLHP.auxElecEIRFoPLRCurveIndex = Curve::GetCurveIndex(state, auxEIRFPLRName);
    3313            7 :                 if (thisPLHP.auxElecEIRFoPLRCurveIndex == 0) {
    3314            0 :                     ShowSevereError(state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, auxEIRFPLRName));
    3315            0 :                     errorsFound = true;
    3316              :                 }
    3317            7 :             } else {
    3318            0 :                 thisPLHP.auxElecEIRFoPLRCurveIndex = 0;
    3319              :             }
    3320              : 
    3321              :             // N13 standby_electric_power
    3322            7 :             auto stdElecPwrFound = fields.find("standby_electric_power");
    3323            7 :             if (stdElecPwrFound != fields.end()) {
    3324            7 :                 thisPLHP.standbyElecPower = stdElecPwrFound.value().get<Real64>();
    3325              :             } else {
    3326            0 :                 Real64 defaultVal = 0.0;
    3327            0 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "standby_electric_power", defaultVal)) {
    3328            0 :                     ShowSevereError(state, "EIR FFHP: standby electric power not entered and could not get default value.");
    3329            0 :                     errorsFound = true;
    3330              :                 } else {
    3331            0 :                     thisPLHP.standbyElecPower = defaultVal;
    3332              :                 }
    3333              :             }
    3334              : 
    3335              :             // N14 minimum unloading ratio
    3336            7 :             auto minimumUnloadingRatio = fields.find("minimum_unloading_ratio");
    3337            7 :             if (minimumUnloadingRatio != fields.end()) {
    3338            0 :                 thisPLHP.minimumUnloadingRatio = minimumUnloadingRatio.value().get<Real64>();
    3339              :             } else {
    3340            7 :                 Real64 defaultVal = 0.25;
    3341           21 :                 if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "minimum_unloading_ratio", defaultVal)) {
    3342            0 :                     ShowSevereError(state, "EIR FFHP: minimum unload ratio not entered and could not get default value.");
    3343            0 :                     errorsFound = true;
    3344              :                 } else {
    3345            7 :                     thisPLHP.minimumUnloadingRatio = defaultVal;
    3346              :                 }
    3347              :             }
    3348            7 :             if (thisPLHP.minimumUnloadingRatio < thisPLHP.minPLR) {
    3349            0 :                 ShowSevereError(state, "EIR FFHP: the minimum unloading ratio cannot be lower than the minimum part load ratio.");
    3350            0 :                 errorsFound = true;
    3351              :             }
    3352              : 
    3353            7 :             bool nodeErrorsFound = false;
    3354            7 :             thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    3355              :                                                                                loadSideInletNodeName,
    3356              :                                                                                nodeErrorsFound,
    3357              :                                                                                objType,
    3358              :                                                                                thisPLHP.name,
    3359              :                                                                                DataLoopNode::NodeFluidType::Water,
    3360              :                                                                                DataLoopNode::ConnectionType::Inlet,
    3361              :                                                                                NodeInputManager::CompFluidStream::Primary,
    3362              :                                                                                DataLoopNode::ObjectIsNotParent);
    3363            7 :             thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    3364              :                                                                                 loadSideOutletNodeName,
    3365              :                                                                                 nodeErrorsFound,
    3366              :                                                                                 objType,
    3367              :                                                                                 thisPLHP.name,
    3368              :                                                                                 DataLoopNode::NodeFluidType::Water,
    3369              :                                                                                 DataLoopNode::ConnectionType::Outlet,
    3370              :                                                                                 NodeInputManager::CompFluidStream::Primary,
    3371              :                                                                                 DataLoopNode::ObjectIsNotParent);
    3372              : 
    3373            7 :             thisPLHP.airSource = true;    // this is always true, at least for now, for Fuel-Fired PlantLoop Heat Pump
    3374            7 :             thisPLHP.waterSource = false; // this is always false, at least for now, for Fuel-Fired PlantLoop Heat Pump
    3375            7 :             thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    3376              :                                                                                  sourceSideInletNodeName,
    3377              :                                                                                  nodeErrorsFound,
    3378              :                                                                                  objType,
    3379              :                                                                                  thisPLHP.name,
    3380              :                                                                                  DataLoopNode::NodeFluidType::Air,
    3381              :                                                                                  DataLoopNode::ConnectionType::OutsideAir,
    3382              :                                                                                  NodeInputManager::CompFluidStream::Secondary,
    3383              :                                                                                  DataLoopNode::ObjectIsNotParent);
    3384            7 :             thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    3385              :                                                                                   sourceSideOutletNodeName,
    3386              :                                                                                   nodeErrorsFound,
    3387              :                                                                                   objType,
    3388              :                                                                                   thisPLHP.name,
    3389              :                                                                                   DataLoopNode::NodeFluidType::Air,
    3390              :                                                                                   DataLoopNode::ConnectionType::OutsideAir,
    3391              :                                                                                   NodeInputManager::CompFluidStream::Secondary,
    3392              :                                                                                   DataLoopNode::ObjectIsNotParent);
    3393              : 
    3394            7 :             if (nodeErrorsFound) {
    3395            0 :                 errorsFound = true;
    3396              :             }
    3397            7 :             BranchNodeConnections::TestCompSet(
    3398            7 :                 state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
    3399              : 
    3400              :             // store the worker functions that generalized the heating/cooling sides
    3401            7 :             thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
    3402            7 :             thisPLHP.calcQsource = classToInput.calcQsource;
    3403            7 :             thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
    3404              : 
    3405            7 :             if (!errorsFound) {
    3406            7 :                 state.dataEIRFuelFiredHeatPump->heatPumps.push_back(thisPLHP);
    3407              :             }
    3408           14 :         }
    3409           12 :     }
    3410            6 :     if (errorsFound) {
    3411              :         ShowFatalError(state, "Previous EIR PLFFHP errors cause program termination."); // LCOV_EXCL_LINE
    3412              :     }
    3413            6 : }
    3414              : 
    3415            5 : void EIRFuelFiredHeatPump::oneTimeInit(EnergyPlusData &state)
    3416              : {
    3417              :     // This function does all the one-time initialization
    3418            5 :     constexpr std::string_view routineName = "EIRFuelFiredHeatPump : oneTimeInit"; // + __FUNCTION__;
    3419              : 
    3420            5 :     if (this->oneTimeInitFlag) {
    3421            5 :         bool errFlag = false;
    3422              : 
    3423              :         // setup output variables
    3424           10 :         SetupOutputVariable(state,
    3425              :                             "Fuel-fired Absorption HeatPump Load Side Heat Transfer Rate",
    3426              :                             Constant::Units::W,
    3427            5 :                             this->loadSideHeatTransfer,
    3428              :                             OutputProcessor::TimeStepType::System,
    3429              :                             OutputProcessor::StoreType::Average,
    3430            5 :                             this->name);
    3431           10 :         SetupOutputVariable(state,
    3432              :                             "Fuel-fired Absorption HeatPump Load Side Heat Transfer Energy",
    3433              :                             Constant::Units::J,
    3434            5 :                             this->loadSideEnergy,
    3435              :                             OutputProcessor::TimeStepType::System,
    3436              :                             OutputProcessor::StoreType::Sum,
    3437            5 :                             this->name,
    3438              :                             Constant::eResource::EnergyTransfer,
    3439              :                             OutputProcessor::Group::Plant);
    3440           10 :         SetupOutputVariable(state,
    3441              :                             "Fuel-fired Absorption HeatPump Inlet Temperature", // "Heat Pump Load Side Inlet Temperature",
    3442              :                             Constant::Units::C,
    3443            5 :                             this->loadSideInletTemp,
    3444              :                             OutputProcessor::TimeStepType::System,
    3445              :                             OutputProcessor::StoreType::Average,
    3446            5 :                             this->name);
    3447           10 :         SetupOutputVariable(state,
    3448              :                             "Fuel-fired Absorption HeatPump Outlet Temperature", // "Heat Pump Load Side Outlet Temperature",
    3449              :                             Constant::Units::C,
    3450            5 :                             this->loadSideOutletTemp,
    3451              :                             OutputProcessor::TimeStepType::System,
    3452              :                             OutputProcessor::StoreType::Average,
    3453            5 :                             this->name);
    3454           10 :         SetupOutputVariable(state,
    3455              :                             "Fuel-fired Absorption HeatPump Fuel Rate",
    3456              :                             Constant::Units::W,
    3457            5 :                             this->fuelRate,
    3458              :                             OutputProcessor::TimeStepType::System,
    3459              :                             OutputProcessor::StoreType::Average,
    3460            5 :                             this->name);
    3461           10 :         SetupOutputVariable(state,
    3462              :                             "Fuel-fired Absorption HeatPump Electricity Rate",
    3463              :                             Constant::Units::W,
    3464            5 :                             this->powerUsage,
    3465              :                             OutputProcessor::TimeStepType::System,
    3466              :                             OutputProcessor::StoreType::Average,
    3467            5 :                             this->name);
    3468            5 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) { // energy from HeatPump:AirToWater:FuelFired:Cooling object
    3469            2 :             SetupOutputVariable(state,
    3470              :                                 "Fuel-fired Absorption HeatPump Fuel Energy",
    3471              :                                 Constant::Units::J,
    3472            1 :                                 this->fuelEnergy,
    3473              :                                 OutputProcessor::TimeStepType::System,
    3474              :                                 OutputProcessor::StoreType::Sum,
    3475            1 :                                 this->name,
    3476            1 :                                 Constant::eFuel2eResource[(int)this->fuelType],
    3477              :                                 OutputProcessor::Group::Plant,
    3478              :                                 OutputProcessor::EndUseCat::Cooling,
    3479              :                                 this->endUseSubcat); //"Heat Pump",
    3480            2 :             SetupOutputVariable(state,
    3481              :                                 "Fuel-fired Absorption HeatPump Electricity Energy",
    3482              :                                 Constant::Units::J,
    3483            1 :                                 this->powerEnergy,
    3484              :                                 OutputProcessor::TimeStepType::System,
    3485              :                                 OutputProcessor::StoreType::Sum,
    3486            1 :                                 this->name,
    3487              :                                 Constant::eResource::Electricity,
    3488              :                                 OutputProcessor::Group::Plant,
    3489              :                                 OutputProcessor::EndUseCat::Cooling,
    3490              :                                 this->endUseSubcat); // "Heat Pump",
    3491            4 :         } else if (this->EIRHPType ==
    3492              :                    DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) { // energy from HeatPump:AirToWater:FuelFired:Heating object
    3493            8 :             SetupOutputVariable(state,
    3494              :                                 "Fuel-fired Absorption HeatPump Fuel Energy",
    3495              :                                 Constant::Units::J,
    3496            4 :                                 this->fuelEnergy,
    3497              :                                 OutputProcessor::TimeStepType::System,
    3498              :                                 OutputProcessor::StoreType::Sum,
    3499            4 :                                 this->name,
    3500            4 :                                 Constant::eFuel2eResource[(int)this->fuelType],
    3501              :                                 OutputProcessor::Group::Plant,
    3502              :                                 OutputProcessor::EndUseCat::Heating,
    3503              :                                 this->endUseSubcat); // "Heat Pump",
    3504            8 :             SetupOutputVariable(state,
    3505              :                                 "Fuel-fired Absorption HeatPump Electricity Energy",
    3506              :                                 Constant::Units::J,
    3507            4 :                                 this->powerEnergy,
    3508              :                                 OutputProcessor::TimeStepType::System,
    3509              :                                 OutputProcessor::StoreType::Sum,
    3510            4 :                                 this->name,
    3511              :                                 Constant::eResource::Electricity,
    3512              :                                 OutputProcessor::Group::Plant,
    3513              :                                 OutputProcessor::EndUseCat::Heating,
    3514              :                                 this->endUseSubcat); // "Heat Pump",
    3515              :         }
    3516           10 :         SetupOutputVariable(state,
    3517              :                             "Fuel-fired Absorption HeatPump Mass Flow Rate",
    3518              :                             Constant::Units::kg_s,
    3519            5 :                             this->loadSideMassFlowRate,
    3520              :                             OutputProcessor::TimeStepType::System,
    3521              :                             OutputProcessor::StoreType::Average,
    3522            5 :                             this->name);
    3523           10 :         SetupOutputVariable(state,
    3524              :                             "Fuel-fired Absorption HeatPump Volumetric Flow Rate",
    3525              :                             Constant::Units::m3_s,
    3526            5 :                             this->loadSideVolumeFlowRate,
    3527              :                             OutputProcessor::TimeStepType::System,
    3528              :                             OutputProcessor::StoreType::Average,
    3529            5 :                             this->name);
    3530           10 :         SetupOutputVariable(state,
    3531              :                             "Fuel-fired Absorption HeatPump Cycling Ratio Fraction",
    3532              :                             Constant::Units::None,
    3533            5 :                             this->cyclingRatioFraction,
    3534              :                             OutputProcessor::TimeStepType::System,
    3535              :                             OutputProcessor::StoreType::Average,
    3536            5 :                             this->name);
    3537              : 
    3538              :         // find this component on the plant
    3539            5 :         bool thisErrFlag = false;
    3540           15 :         PlantUtilities::ScanPlantLoopsForObject(
    3541           10 :             state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
    3542              : 
    3543            5 :         if (thisErrFlag) {
    3544            0 :             ShowSevereError(state,
    3545            0 :                             format("{}: Plant topology problem for {} name = \"{}\"",
    3546              :                                    routineName,
    3547            0 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    3548            0 :                                    this->name));
    3549            0 :             ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
    3550            0 :             errFlag = true;
    3551            5 :         } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
    3552            0 :             ShowSevereError(state,
    3553            0 :                             format("{}: Invalid connections for {} name = \"{}\"",
    3554              :                                    routineName,
    3555            0 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    3556            0 :                                    this->name));
    3557            0 :             ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
    3558            0 :             errFlag = true;
    3559              :         }
    3560              : 
    3561            5 :         thisErrFlag = false;
    3562            5 :         if (this->waterSource) {
    3563            0 :             PlantUtilities::ScanPlantLoopsForObject(
    3564            0 :                 state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
    3565              : 
    3566            0 :             if (thisErrFlag) {
    3567            0 :                 ShowSevereError(state,
    3568            0 :                                 format("{}: Plant topology problem for {} name = \"{}\"",
    3569              :                                        routineName,
    3570            0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    3571            0 :                                        this->name));
    3572            0 :                 ShowContinueError(state, "Could not locate component's source side connections on a plant loop.");
    3573            0 :                 errFlag = true;
    3574            0 :             } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
    3575            0 :                 ShowSevereError(state,
    3576            0 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    3577              :                                        routineName,
    3578            0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    3579            0 :                                        this->name));
    3580            0 :                 ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop.");
    3581            0 :                 errFlag = true;
    3582              :             }
    3583              : 
    3584              :             // make sure it is not the same loop on both sides.
    3585            0 :             if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
    3586            0 :                 ShowSevereError(state,
    3587            0 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    3588              :                                        routineName,
    3589            0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    3590            0 :                                        this->name));
    3591            0 :                 ShowContinueError(state, "The load and source sides need to be on different loops.");
    3592            0 :                 errFlag = true;
    3593              :             } else {
    3594              : 
    3595            0 :                 PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
    3596              :             }
    3597            5 :         } else if (this->airSource) {
    3598              :             // nothing to do here ?
    3599              :         }
    3600              : 
    3601            5 :         if (errFlag) {
    3602            0 :             ShowFatalError(state, format("{}: Program terminated due to previous condition(s).", routineName));
    3603              :         }
    3604            5 :         this->oneTimeInitFlag = false;
    3605              :     }
    3606            5 : }
    3607              : 
    3608           18 : void EIRFuelFiredHeatPump::report(EnergyPlusData &state)
    3609              : {
    3610           18 :     Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
    3611              : 
    3612           18 :     this->fuelEnergy = this->fuelRate * reportingInterval;
    3613           18 :     this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
    3614           18 :     this->powerEnergy = this->powerUsage * reportingInterval;
    3615           18 :     this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
    3616              : 
    3617              :     // update nodes
    3618           18 :     PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
    3619           18 :     state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
    3620           18 :     state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
    3621           18 : }
    3622              : 
    3623            0 : Real64 EIRFuelFiredHeatPump::getDynamicMaxCapacity(EnergyPlusData &state)
    3624              : {
    3625              :     // Source (air) side temperature variable
    3626            0 :     auto &thisSourceSideInletNode = state.dataLoopNodes->Node(this->sourceSideNodes.inlet);
    3627              :     Real64 oaTempforCurve =
    3628            0 :         (this->oaTempCurveInputVar == OATempCurveVar::WetBulb)
    3629            0 :             ? Psychrometrics::PsyTwbFnTdbWPb(
    3630              :                   state, thisSourceSideInletNode.Temp, thisSourceSideInletNode.HumRat, thisSourceSideInletNode.Press, "PLFFHPEIR::doPhysics()")
    3631            0 :             : thisSourceSideInletNode.Temp;
    3632              : 
    3633              :     // Load (water) side temperature variable
    3634            0 :     Real64 waterTempforCurve = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
    3635            0 :     if (this->waterTempCurveInputVar == WaterTempCurveVar::LeavingCondenser || this->waterTempCurveInputVar == WaterTempCurveVar::LeavingEvaporator) {
    3636            0 :         if (this->flowMode == DataPlant::FlowMode::LeavingSetpointModulated) {
    3637            0 :             auto &thisLoadSideOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
    3638            0 :             if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
    3639            0 :                 waterTempforCurve = thisLoadSideOutletNode.TempSetPoint;
    3640              :             } else {
    3641            0 :                 if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
    3642            0 :                     waterTempforCurve = thisLoadSideOutletNode.TempSetPointHi;
    3643              :                 } else {
    3644            0 :                     waterTempforCurve = thisLoadSideOutletNode.TempSetPointLo;
    3645              :                 }
    3646              :             }
    3647              :         } else {
    3648              :             // If not SP modulated then use actual outlet temp from last iteration?
    3649            0 :             waterTempforCurve = state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp;
    3650              :         }
    3651              :     }
    3652              : 
    3653              :     // evaluate capacity modifier curve and determine load side heat transfer
    3654            0 :     Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve);
    3655            0 :     return this->referenceCapacity * capacityModifierFuncTemp;
    3656              : }
    3657              : 
    3658              : } // namespace EnergyPlus::EIRPlantLoopHeatPumps
        

Generated by: LCOV version 2.0-1