LCOV - code coverage report
Current view: top level - EnergyPlus - PlantLoopHeatPumpEIR.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 67.5 % 1935 1307
Test Date: 2025-06-02 07:23:51 Functions: 94.0 % 50 47

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

Generated by: LCOV version 2.0-1