LCOV - code coverage report
Current view: top level - EnergyPlus - PlantLoopHeatPumpEIR.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1346 1978 68.0 %
Date: 2024-08-24 18:31:18 Functions: 46 47 97.9 %

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

Generated by: LCOV version 1.14