LCOV - code coverage report
Current view: top level - EnergyPlus - PlantLoopHeatPumpEIR.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 67.8 % 1946 1319
Test Date: 2025-05-22 16:09:37 Functions: 89.8 % 49 44

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

Generated by: LCOV version 2.0-1