LCOV - code coverage report
Current view: top level - EnergyPlus - PlantLoopHeatPumpEIR.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 391 613 63.8 %
Date: 2023-01-17 19:17:23 Functions: 21 21 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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 <string>
      50             : #include <utility>
      51             : #include <vector>
      52             : 
      53             : // EnergyPlus headers
      54             : #include <EnergyPlus/Autosizing/Base.hh>
      55             : #include <EnergyPlus/BranchNodeConnections.hh>
      56             : #include <EnergyPlus/CurveManager.hh>
      57             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58             : #include <EnergyPlus/DataEnvironment.hh>
      59             : #include <EnergyPlus/DataHVACGlobals.hh>
      60             : #include <EnergyPlus/DataIPShortCuts.hh>
      61             : #include <EnergyPlus/DataLoopNode.hh>
      62             : #include <EnergyPlus/DataSizing.hh>
      63             : #include <EnergyPlus/FluidProperties.hh>
      64             : #include <EnergyPlus/General.hh>
      65             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      66             : #include <EnergyPlus/NodeInputManager.hh>
      67             : #include <EnergyPlus/OutputProcessor.hh>
      68             : #include <EnergyPlus/OutputReportPredefined.hh>
      69             : #include <EnergyPlus/Plant/DataPlant.hh>
      70             : #include <EnergyPlus/PlantComponent.hh>
      71             : #include <EnergyPlus/PlantLoopHeatPumpEIR.hh>
      72             : #include <EnergyPlus/PlantUtilities.hh>
      73             : #include <EnergyPlus/Psychrometrics.hh>
      74             : #include <EnergyPlus/UtilityRoutines.hh>
      75             : 
      76             : namespace EnergyPlus::EIRPlantLoopHeatPumps {
      77             : 
      78        4946 : void EIRPlantLoopHeatPump::simulate(
      79             :     EnergyPlusData &state, const EnergyPlus::PlantLocation &calledFromLocation, bool const FirstHVACIteration, Real64 &CurLoad, bool const RunFlag)
      80             : {
      81             : 
      82             :     // Call initialize to set flow rates, run flag, and entering temperatures
      83        4946 :     this->running = RunFlag;
      84             : 
      85        4946 :     this->loadSideInletTemp = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
      86        4946 :     this->sourceSideInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
      87             : 
      88        4946 :     if (this->waterSource) {
      89        3304 :         this->setOperatingFlowRatesWSHP(state);
      90        3304 :         if (calledFromLocation.loopNum == this->sourceSidePlantLoc.loopNum) { // condenser side
      91        1646 :             PlantUtilities::UpdateChillerComponentCondenserSide(state,
      92             :                                                                 this->sourceSidePlantLoc.loopNum,
      93             :                                                                 this->sourceSidePlantLoc.loopSideNum,
      94             :                                                                 this->EIRHPType,
      95             :                                                                 this->sourceSideNodes.inlet,
      96             :                                                                 this->sourceSideNodes.outlet,
      97             :                                                                 this->sourceSideHeatTransfer,
      98             :                                                                 this->sourceSideInletTemp,
      99             :                                                                 this->sourceSideOutletTemp,
     100             :                                                                 this->sourceSideMassFlowRate,
     101             :                                                                 FirstHVACIteration);
     102        1646 :             return;
     103             :         }
     104        1642 :     } else if (this->airSource) {
     105        1642 :         this->setOperatingFlowRatesASHP(state);
     106             :     }
     107             : 
     108        3300 :     if (this->running) {
     109        1608 :         this->doPhysics(state, CurLoad);
     110             :     } else {
     111        1692 :         this->resetReportingVariables();
     112             :     }
     113             : 
     114             :     // update nodes
     115        3300 :     state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
     116        3300 :     state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
     117             : }
     118             : 
     119        1608 : Real64 EIRPlantLoopHeatPump::getLoadSideOutletSetPointTemp(EnergyPlusData &state) const
     120             : {
     121        1608 :     auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum);
     122        1608 :     auto &thisLoadLoopSide = thisLoadPlantLoop.LoopSide(this->loadSidePlantLoc.loopSideNum);
     123        1608 :     auto &thisLoadBranch = thisLoadLoopSide.Branch(this->loadSidePlantLoc.branchNum);
     124        1608 :     auto &thisLoadComp = thisLoadBranch.Comp(this->loadSidePlantLoc.compNum);
     125        1608 :     if (thisLoadPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     126        1608 :         if (thisLoadComp.CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
     127             :             // there will be a valid set-point on outlet
     128           0 :             return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
     129             :         } else { // use plant loop overall set-point
     130        1608 :             return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPoint;
     131             :         }
     132           0 :     } else if (thisLoadPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
     133           0 :         if (thisLoadComp.CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
     134             :             // there will be a valid set-point on outlet
     135           0 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
     136           0 :                 return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointHi;
     137             :             } else {
     138           0 :                 return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointLo;
     139             :             }
     140             :         } else { // use plant loop overall set-point
     141           0 :             if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
     142           0 :                 return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPointHi;
     143             :             } else {
     144           0 :                 return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPointLo;
     145             :             }
     146             :         }
     147             :     } else {
     148             :         // there's no other enums for loop demand calcs, so I don't have a reasonable unit test for these
     149             :         // lines, they simply should not be able to get here.  But a fatal is here anyway just in case,
     150             :         // and the lines are excluded from coverage.
     151             :         ShowFatalError(state, "Unsupported loop demand calculation scheme in EIR heat pump"); // LCOV_EXCL_LINE
     152             :         return -999; // not actually returned with Fatal Error call above  // LCOV_EXCL_LINE
     153             :     }
     154             : }
     155             : 
     156        1692 : void EIRPlantLoopHeatPump::resetReportingVariables()
     157             : {
     158        1692 :     this->loadSideHeatTransfer = 0.0;
     159        1692 :     this->loadSideEnergy = 0.0;
     160        1692 :     this->loadSideOutletTemp = this->loadSideInletTemp;
     161        1692 :     this->powerUsage = 0.0;
     162        1692 :     this->powerEnergy = 0.0;
     163        1692 :     this->sourceSideHeatTransfer = 0.0;
     164        1692 :     this->sourceSideOutletTemp = this->sourceSideInletTemp;
     165        1692 :     this->sourceSideEnergy = 0.0;
     166        1692 : }
     167             : 
     168        3304 : void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state)
     169             : {
     170        3304 :     if (!this->running) {
     171         846 :         this->loadSideMassFlowRate = 0.0;
     172         846 :         this->sourceSideMassFlowRate = 0.0;
     173         846 :         PlantUtilities::SetComponentFlowRate(
     174             :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     175         846 :         PlantUtilities::SetComponentFlowRate(
     176             :             state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     177         846 :         PlantUtilities::PullCompInterconnectTrigger(state,
     178             :                                                     this->loadSidePlantLoc,
     179             :                                                     this->condMassFlowRateTriggerIndex,
     180             :                                                     this->sourceSidePlantLoc,
     181             :                                                     DataPlant::CriteriaType::MassFlowRate,
     182             :                                                     this->sourceSideMassFlowRate);
     183             :         // Set flows if the heat pump is running
     184             :     } else { // the heat pump must run
     185        2458 :         this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
     186        2458 :         this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
     187        2458 :         PlantUtilities::SetComponentFlowRate(
     188             :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     189        2458 :         PlantUtilities::SetComponentFlowRate(
     190             :             state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     191             : 
     192             :         // if there's no flow in one, try to turn the entire heat pump off
     193        2458 :         if (this->loadSideMassFlowRate <= 0.0 || this->sourceSideMassFlowRate <= 0.0) {
     194         849 :             this->loadSideMassFlowRate = 0.0;
     195         849 :             this->sourceSideMassFlowRate = 0.0;
     196         849 :             this->running = false;
     197         849 :             PlantUtilities::SetComponentFlowRate(
     198             :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     199         849 :             PlantUtilities::SetComponentFlowRate(
     200             :                 state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
     201             :         }
     202        2458 :         PlantUtilities::PullCompInterconnectTrigger(state,
     203             :                                                     this->loadSidePlantLoc,
     204             :                                                     this->condMassFlowRateTriggerIndex,
     205             :                                                     this->sourceSidePlantLoc,
     206             :                                                     DataPlant::CriteriaType::MassFlowRate,
     207             :                                                     this->sourceSideMassFlowRate);
     208             :     }
     209        3304 : }
     210             : 
     211        1642 : void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state)
     212             : {
     213        1642 :     if (!this->running) {
     214         834 :         this->loadSideMassFlowRate = 0.0;
     215         834 :         this->sourceSideMassFlowRate = 0.0;
     216         834 :         PlantUtilities::SetComponentFlowRate(
     217             :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     218             :         // Set flows if the heat pump is running
     219             :     } else { // the heat pump must run
     220         808 :         this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
     221         808 :         this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
     222         808 :         PlantUtilities::SetComponentFlowRate(
     223             :             state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     224             : 
     225             :         // if there's no flow in one, try to turn the entire heat pump off
     226         808 :         if (this->loadSideMassFlowRate <= 0.0) {
     227           0 :             this->loadSideMassFlowRate = 0.0;
     228           0 :             this->sourceSideMassFlowRate = 0.0;
     229           0 :             this->running = false;
     230           0 :             PlantUtilities::SetComponentFlowRate(
     231             :                 state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
     232             :         }
     233             :     }
     234        1642 : }
     235             : 
     236        1608 : void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
     237             : {
     238             : 
     239        1608 :     Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
     240             : 
     241             :     // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
     242             :     // 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
     243        3216 :     if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling && currentLoad >= 0.0) ||
     244        2406 :         (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && currentLoad <= 0.0)) {
     245           0 :         this->resetReportingVariables();
     246           0 :         return;
     247             :     }
     248             : 
     249             :     // get setpoint on the load side outlet
     250        1608 :     Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
     251             : 
     252             :     // evaluate capacity modifier curve and determine load side heat transfer
     253        1608 :     Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->sourceSideInletTemp);
     254             : 
     255        1608 :     if (capacityModifierFuncTemp < 0.0) {
     256           0 :         if (this->capModFTErrorIndex == 0) {
     257           0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     258           0 :             ShowContinueError(state,
     259           0 :                               format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
     260           0 :             ShowContinueError(state,
     261           0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
     262             :                                      loadSideOutletSetpointTemp,
     263           0 :                                      this->sourceSideInletTemp));
     264           0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     265             :         }
     266           0 :         ShowRecurringWarningErrorAtEnd(state,
     267           0 :                                        format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
     268           0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     269           0 :                                               this->name),
     270             :                                        this->capModFTErrorIndex,
     271             :                                        capacityModifierFuncTemp,
     272             :                                        capacityModifierFuncTemp);
     273           0 :         capacityModifierFuncTemp = 0.0;
     274             :     }
     275             : 
     276        1608 :     Real64 availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
     277        1608 :     Real64 partLoadRatio = 0.0;
     278        1608 :     if (availableCapacity > 0) {
     279        1608 :         partLoadRatio = max(0.0, min(std::abs(currentLoad) / availableCapacity, 1.0));
     280             :     }
     281             : 
     282             :     // evaluate the actual current operating load side heat transfer rate
     283        1608 :     auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum);
     284        4824 :     Real64 CpLoad = FluidProperties::GetSpecificHeatGlycol(state,
     285             :                                                            thisLoadPlantLoop.FluidName,
     286        1608 :                                                            state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp,
     287             :                                                            thisLoadPlantLoop.FluidIndex,
     288        3216 :                                                            "PLHPEIR::simulate()");
     289        1608 :     this->loadSideHeatTransfer = availableCapacity * partLoadRatio;
     290        1608 :     this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
     291             : 
     292             :     // calculate load side outlet conditions
     293        1608 :     Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
     294        1608 :     this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
     295             : 
     296             :     // calculate power usage from EIR curves
     297        1608 :     Real64 eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
     298             : 
     299        1608 :     if (eirModifierFuncTemp < 0.0) {
     300           0 :         if (this->eirModFTErrorIndex == 0) {
     301           0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     302           0 :             ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
     303           0 :             ShowContinueError(state,
     304           0 :                               format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
     305             :                                      this->loadSideOutletTemp,
     306           0 :                                      this->sourceSideInletTemp));
     307           0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     308             :         }
     309           0 :         ShowRecurringWarningErrorAtEnd(state,
     310           0 :                                        format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
     311           0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     312           0 :                                               this->name),
     313             :                                        this->eirModFTErrorIndex,
     314             :                                        eirModifierFuncTemp,
     315             :                                        eirModifierFuncTemp);
     316           0 :         eirModifierFuncTemp = 0.0;
     317             :     }
     318             : 
     319        1608 :     Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, partLoadRatio);
     320             : 
     321        1608 :     if (eirModifierFuncPLR < 0.0) {
     322           0 :         if (this->eirModFPLRErrorIndex == 0) {
     323           0 :             ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
     324           0 :             ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
     325           0 :             ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", partLoadRatio));
     326           0 :             ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
     327             :         }
     328           0 :         ShowRecurringWarningErrorAtEnd(state,
     329           0 :                                        format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
     330           0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
     331           0 :                                               this->name),
     332             :                                        this->eirModFPLRErrorIndex,
     333             :                                        eirModifierFuncPLR,
     334             :                                        eirModifierFuncPLR);
     335           0 :         eirModifierFuncPLR = 0.0;
     336             :     }
     337             : 
     338        1608 :     this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp;
     339        1608 :     this->powerEnergy = this->powerUsage * reportingInterval;
     340             : 
     341             :     // energy balance on heat pump
     342        1608 :     this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
     343        1608 :     this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
     344             : 
     345             :     // calculate source side outlet conditions
     346        1608 :     Real64 CpSrc = 0.0;
     347        1608 :     if (this->waterSource) {
     348        1600 :         CpSrc = FluidProperties::GetSpecificHeatGlycol(state,
     349             :                                                        thisLoadPlantLoop.FluidName,
     350         800 :                                                        state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp,
     351             :                                                        thisLoadPlantLoop.FluidIndex,
     352         800 :                                                        "PLHPEIR::simulate()");
     353         808 :     } else if (this->airSource) {
     354         808 :         CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
     355             :     }
     356        1608 :     Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
     357        1608 :     this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
     358             : }
     359             : 
     360          30 : void EIRPlantLoopHeatPump::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
     361             : {
     362             :     // This function does all one-time and begin-environment initialization
     363          30 :     std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
     364             : 
     365          30 :     this->oneTimeInit(state); // plant setup
     366             : 
     367          30 :     if (state.dataGlobal->BeginEnvrnFlag && this->envrnInit && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     368           4 :         if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
     369           4 :             this->sizeLoadSide(state);
     370           4 :             if (this->waterSource) {
     371           2 :                 this->sizeSrcSideWSHP(state);
     372           2 :             } else if (this->airSource) {
     373           2 :                 this->sizeSrcSideASHP(state);
     374             :             }
     375             :         }
     376             : 
     377          12 :         Real64 rho = FluidProperties::GetDensityGlycol(state,
     378           4 :                                                        state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
     379             :                                                        DataGlobalConstants::InitConvTemp,
     380           4 :                                                        state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
     381           4 :                                                        routineName);
     382           4 :         this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
     383           4 :         PlantUtilities::InitComponentNodes(state, 0.0, this->loadSideDesignMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
     384             : 
     385           4 :         if (this->waterSource) {
     386           6 :             rho = FluidProperties::GetDensityGlycol(state,
     387           2 :                                                     state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).FluidName,
     388             :                                                     DataGlobalConstants::InitConvTemp,
     389           2 :                                                     state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).FluidIndex,
     390             :                                                     routineName);
     391           2 :             this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
     392           2 :             PlantUtilities::InitComponentNodes(
     393             :                 state, 0.0, this->sourceSideDesignMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
     394           2 :         } else if (this->airSource) {
     395           2 :             rho = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, state.dataEnvrn->OutDryBulbTemp, 0.0, routineName);
     396           2 :             this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
     397             :         }
     398             : 
     399           4 :         this->envrnInit = false;
     400             :     }
     401          30 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     402           0 :         this->envrnInit = true;
     403             :     }
     404          30 : }
     405             : 
     406          30 : void EIRPlantLoopHeatPump::getDesignCapacities(
     407             :     [[maybe_unused]] EnergyPlusData &state, const PlantLocation &calledFromLocation, Real64 &MaxLoad, Real64 &MinLoad, Real64 &OptLoad)
     408             : {
     409          30 :     if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
     410          20 :         MinLoad = 0.0;
     411          20 :         MaxLoad = this->referenceCapacity;
     412          20 :         OptLoad = this->referenceCapacity;
     413             :     } else {
     414          10 :         MinLoad = 0.0;
     415          10 :         MaxLoad = 0.0;
     416          10 :         OptLoad = 0.0;
     417             :     }
     418          30 : }
     419             : 
     420           4 : void EIRPlantLoopHeatPump::sizeLoadSide(EnergyPlusData &state)
     421             : {
     422             :     // Tries to size the load side flow rate and capacity, source side flow, and the rated power usage
     423             :     // There are two major sections to this function, one if plant sizing is available, and one if not
     424             :     // If plant sizing is available, then we can generate sizes for the equipment.  This is done for not-only
     425             :     //   autosized fields, but also hard-sized fields so that we can report out significant deviations between
     426             :     //   the two values.
     427             :     // If plant sizing is not available, it tries to use a companion heat pump coil to do sizing
     428             : 
     429           4 :     bool errorsFound = false;
     430             : 
     431             :     // these variables will be used throughout this function as a temporary value of that physical state
     432           4 :     Real64 tmpCapacity = this->referenceCapacity;
     433           4 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
     434             : 
     435           4 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
     436           4 :     Real64 loadSideInitTemp = DataGlobalConstants::CWInitConvTemp;
     437           4 :     if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
     438           2 :         loadSideInitTemp = DataGlobalConstants::HWInitConvTemp;
     439             :     }
     440             : 
     441          12 :     Real64 const rho = FluidProperties::GetDensityGlycol(state,
     442           4 :                                                          state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
     443             :                                                          loadSideInitTemp,
     444           4 :                                                          state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
     445           8 :                                                          "EIRPlantLoopHeatPump::size()");
     446          12 :     Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(state,
     447           4 :                                                              state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
     448             :                                                              loadSideInitTemp,
     449           4 :                                                              state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
     450           8 :                                                              "EIRPlantLoopHeatPump::size()");
     451             : 
     452           4 :     int pltLoadSizNum = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).PlantSizNum;
     453           4 :     if (pltLoadSizNum > 0) {
     454             :         // this first IF block is really just about calculating the local tmpCapacity and tmpLoadVolFlow values
     455             :         // these represent what the unit would size those to, whether it is doing auto-sizing or not
     456           0 :         if (state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate > DataHVACGlobals::SmallWaterVolFlow) {
     457           0 :             tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate * this->sizingFactor;
     458           0 :             if (this->companionHeatPumpCoil) {
     459           0 :                 tmpLoadVolFlow = max(tmpLoadVolFlow, this->companionHeatPumpCoil->loadSideDesignVolFlowRate);
     460           0 :                 if (this->loadSideDesignVolFlowRateWasAutoSized) this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
     461             :             }
     462           0 :             tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow;
     463           0 :         } else if (this->companionHeatPumpCoil && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
     464           0 :             tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
     465           0 :             tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow;
     466             :         } else {
     467           0 :             if (this->referenceCapacityWasAutoSized) tmpCapacity = 0.0;
     468           0 :             if (this->loadSideDesignVolFlowRateWasAutoSized) tmpLoadVolFlow = 0.0;
     469             :         }
     470             :         // now we actually need to store and report out the values
     471           0 :         if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     472             :             // handle the auto-sizable reference capacity
     473           0 :             if (this->referenceCapacityWasAutoSized) {
     474             :                 // if auto-sized, we just need to store the sized value and then report out the capacity when plant is ready
     475           0 :                 this->referenceCapacity = tmpCapacity;
     476           0 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     477           0 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
     478             :                 }
     479           0 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     480           0 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
     481             :                 }
     482             :             } else {
     483             :                 // this blocks means the capacity value was hard-sized
     484           0 :                 if (this->referenceCapacity > 0.0 && tmpCapacity > 0.0) {
     485             :                     // then the capacity was hard-sized to a good value and the tmpCapacity was calculated to a good value too
     486           0 :                     Real64 hardSizedCapacity = this->referenceCapacity;
     487           0 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     488           0 :                         if (state.dataGlobal->DoPlantSizing) {
     489           0 :                             BaseSizer::reportSizerOutput(state,
     490             :                                                          typeName,
     491             :                                                          this->name,
     492             :                                                          "Design Size Nominal Capacity [W]",
     493             :                                                          tmpCapacity,
     494             :                                                          "User-Specified Nominal Capacity [W]",
     495           0 :                                                          hardSizedCapacity);
     496             :                         } else {
     497           0 :                             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", hardSizedCapacity);
     498             :                         }
     499             :                         // we can warn here if there is a bit mismatch between hard- and auto-sized
     500           0 :                         if (state.dataGlobal->DisplayExtraWarnings) {
     501           0 :                             if ((std::abs(tmpCapacity - hardSizedCapacity) / hardSizedCapacity) > state.dataSize->AutoVsHardSizingThreshold) {
     502           0 :                                 ShowWarningMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
     503           0 :                                 ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", hardSizedCapacity));
     504           0 :                                 ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpCapacity));
     505           0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     506           0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     507             :                             }
     508             :                         }
     509             :                     }
     510             :                     // moving forward with more calculations, we need to update the 'tmp' capacity to the hard-sized value
     511           0 :                     tmpCapacity = hardSizedCapacity;
     512             :                 }
     513             :             }
     514             :             // now handle the auto-sizable load side flow rate
     515           0 :             if (this->loadSideDesignVolFlowRateWasAutoSized) {
     516           0 :                 this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
     517           0 :                 this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
     518           0 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     519           0 :                     BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
     520             :                 }
     521           0 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     522           0 :                     BaseSizer::reportSizerOutput(
     523           0 :                         state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
     524             :                 }
     525             :             } else {
     526           0 :                 if (this->loadSideDesignVolFlowRate > 0.0 && tmpLoadVolFlow > 0.0) {
     527           0 :                     Real64 hardSizedLoadSideFlow = this->loadSideDesignVolFlowRate;
     528           0 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     529           0 :                         if (state.dataGlobal->DoPlantSizing) {
     530           0 :                             BaseSizer::reportSizerOutput(state,
     531             :                                                          typeName,
     532             :                                                          this->name,
     533             :                                                          "Design Size Load Side Volume Flow Rate [m3/s]",
     534             :                                                          tmpLoadVolFlow,
     535             :                                                          "User-Specified Load Side Volume Flow Rate [m3/s]",
     536           0 :                                                          hardSizedLoadSideFlow);
     537             :                         } else {
     538           0 :                             BaseSizer::reportSizerOutput(
     539           0 :                                 state, typeName, this->name, "User-Specified Load Side Volume Flow Rate [m3/s]", hardSizedLoadSideFlow);
     540             :                         }
     541           0 :                         if (state.dataGlobal->DisplayExtraWarnings) {
     542           0 :                             if ((std::abs(tmpLoadVolFlow - hardSizedLoadSideFlow) / hardSizedLoadSideFlow) >
     543           0 :                                 state.dataSize->AutoVsHardSizingThreshold) {
     544           0 :                                 ShowMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
     545           0 :                                 ShowContinueError(state, format("User-Specified Load Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedLoadSideFlow));
     546           0 :                                 ShowContinueError(state,
     547           0 :                                                   format("differs from Design Size Load Side Volume Flow Rate of {:.2R} [m3/s]", tmpLoadVolFlow));
     548           0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     549           0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     550             :                             }
     551             :                         }
     552             :                     }
     553           0 :                     tmpLoadVolFlow = hardSizedLoadSideFlow;
     554             :                 }
     555             :             }
     556             :         }
     557             :     } else {
     558             :         // no plant sizing available...try to use the companion coil
     559           4 :         if (this->companionHeatPumpCoil) {
     560           4 :             if (this->companionHeatPumpCoil->loadSideDesignVolFlowRateWasAutoSized && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
     561           0 :                 tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
     562           0 :                 if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     563           0 :                     this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
     564           0 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     565           0 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
     566             :                     }
     567           0 :                     if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     568           0 :                         BaseSizer::reportSizerOutput(
     569           0 :                             state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
     570             :                     }
     571             :                 }
     572             :             }
     573           4 :             if (this->companionHeatPumpCoil->referenceCapacityWasAutoSized && this->companionHeatPumpCoil->referenceCapacity > 0.0) {
     574           0 :                 tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
     575           0 :                 if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     576           0 :                     this->referenceCapacity = tmpCapacity;
     577           0 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     578           0 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
     579             :                     }
     580           0 :                     if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     581           0 :                         BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
     582             :                     }
     583             :                 }
     584             :             }
     585             :         } else {
     586             :             // no companion coil, and no plant sizing, so can't do anything
     587           0 :             if ((this->loadSideDesignVolFlowRateWasAutoSized || this->referenceCapacityWasAutoSized) &&
     588           0 :                 state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     589           0 :                 ShowSevereError(state, "EIRPlantLoopHeatPump::size(): Autosizing requires a loop Sizing:Plant object.");
     590           0 :                 ShowContinueError(state, "Occurs in HeatPump:PlantLoop:EquationFit:Cooling object = " + this->name);
     591           0 :                 errorsFound = true;
     592             :             }
     593             :         }
     594           4 :         if (!this->loadSideDesignVolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
     595           4 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Load Side Flow Rate [m3/s]", this->loadSideDesignVolFlowRate);
     596             :         }
     597           4 :         if (!this->referenceCapacityWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
     598           4 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", this->referenceCapacity);
     599             :         }
     600             :     }
     601           4 :     if (errorsFound) {
     602           0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
     603             :     }
     604           4 : }
     605             : 
     606           2 : void EIRPlantLoopHeatPump::sizeSrcSideWSHP(EnergyPlusData &state)
     607             : {
     608             :     // size the source-side for the water-source HP
     609           2 :     bool errorsFound = false;
     610             : 
     611             :     // these variables will be used throughout this function as a temporary value of that physical state
     612           2 :     Real64 tmpCapacity = this->referenceCapacity;
     613           2 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
     614             :     Real64 tmpSourceVolFlow;
     615             : 
     616           2 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
     617           2 :     Real64 sourceSideInitTemp = DataGlobalConstants::HWInitConvTemp;
     618           2 :     if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
     619           1 :         sourceSideInitTemp = DataGlobalConstants::CWInitConvTemp;
     620             :     }
     621             : 
     622           6 :     Real64 const rhoSrc = FluidProperties::GetDensityGlycol(state,
     623           2 :                                                             state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
     624             :                                                             sourceSideInitTemp,
     625           2 :                                                             state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
     626           4 :                                                             "EIRPlantLoopHeatPump::size()");
     627           6 :     Real64 const CpSrc = FluidProperties::GetSpecificHeatGlycol(state,
     628           2 :                                                                 state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
     629             :                                                                 sourceSideInitTemp,
     630           2 :                                                                 state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
     631           4 :                                                                 "EIRPlantLoopHeatPump::size()");
     632             : 
     633             :     // To start we need to override the calculated load side flow
     634             :     // rate if it was actually hard-sized
     635           2 :     if (!this->loadSideDesignVolFlowRateWasAutoSized) tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
     636             : 
     637             :     // calculate an auto-sized value for source design flow regardless of whether it was auto-sized or not
     638           2 :     int plantSourceSizingIndex = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).PlantSizNum;
     639           2 :     if (plantSourceSizingIndex > 0) {
     640             :         // to get the source flow, we first must calculate the required heat impact on the source side
     641             :         // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
     642             :         // Then the energy balance:     Qsrc = Qload + Power
     643             :         // Substituting for Power:      Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
     644           0 :         Real64 const designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
     645             :         // To get the design source flow rate, just apply the sensible heat rate equation:
     646             :         //                              Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
     647             :         //                              Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
     648           0 :         tmpSourceVolFlow = designSourceSideHeatTransfer / (state.dataSize->PlantSizData(plantSourceSizingIndex).DeltaT * CpSrc * rhoSrc);
     649             :     } else {
     650             :         // just assume it's the same as the load side if we don't have any sizing information
     651           2 :         tmpSourceVolFlow = tmpLoadVolFlow;
     652             :     }
     653           2 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
     654           0 :         this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
     655           0 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     656           0 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
     657             :         }
     658           0 :         if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     659           0 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
     660             :         }
     661             :     } else {
     662             :         // source design flow was hard-sized
     663           2 :         if (this->sourceSideDesignVolFlowRate > 0.0 && tmpSourceVolFlow > 0.0) {
     664           2 :             Real64 const hardSizedSourceSideFlow = this->sourceSideDesignVolFlowRate;
     665           2 :             if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     666           2 :                 if (state.dataGlobal->DoPlantSizing) {
     667           0 :                     BaseSizer::reportSizerOutput(state,
     668             :                                                  typeName,
     669             :                                                  this->name,
     670             :                                                  "Design Size Source Side Volume Flow Rate [m3/s]",
     671             :                                                  tmpSourceVolFlow,
     672             :                                                  "User-Specified Source Side Volume Flow Rate [m3/s]",
     673           0 :                                                  hardSizedSourceSideFlow);
     674             :                 } else {
     675           4 :                     BaseSizer::reportSizerOutput(
     676           2 :                         state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", hardSizedSourceSideFlow);
     677             :                 }
     678           2 :                 if (state.dataGlobal->DisplayExtraWarnings) {
     679           0 :                     if ((std::abs(tmpSourceVolFlow - hardSizedSourceSideFlow) / hardSizedSourceSideFlow) >
     680           0 :                         state.dataSize->AutoVsHardSizingThreshold) {
     681           0 :                         ShowMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
     682           0 :                         ShowContinueError(state, format("User-Specified Source Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedSourceSideFlow));
     683           0 :                         ShowContinueError(state, format("differs from Design Size Source Side Volume Flow Rate of {:.2R} [m3/s]", tmpSourceVolFlow));
     684           0 :                         ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     685           0 :                         ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     686             :                     }
     687             :                 }
     688             :             }
     689           2 :             tmpSourceVolFlow = hardSizedSourceSideFlow;
     690             :         }
     691             :     }
     692             : 
     693             :     // skipping autosized power section
     694             : 
     695             :     // register the design volume flows with the plant, only doing half of source because the companion
     696             :     // is generally on the same loop
     697           2 :     PlantUtilities::RegisterPlantCompDesignFlow(state, this->loadSideNodes.inlet, tmpLoadVolFlow);
     698           2 :     PlantUtilities::RegisterPlantCompDesignFlow(state, this->sourceSideNodes.inlet, tmpSourceVolFlow / 0.5);
     699             : 
     700           2 :     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     701             :         // create predefined report
     702           2 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, this->name, typeName);
     703           2 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, this->name, this->referenceCOP);
     704           2 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->name, this->referenceCapacity);
     705             :     }
     706             : 
     707           2 :     if (errorsFound) {
     708           0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
     709             :     }
     710           2 : }
     711             : 
     712           2 : void EIRPlantLoopHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
     713             : {
     714             :     // size the source-side for the air-source HP
     715           2 :     bool errorsFound = false;
     716             : 
     717             :     // these variables will be used throughout this function as a temporary value of that physical state
     718           2 :     Real64 tmpCapacity = this->referenceCapacity;
     719           2 :     Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
     720           2 :     Real64 tmpSourceVolFlow = 0.0;
     721             : 
     722             :     // will leave like this for now
     723             :     // need to update these to better values later
     724           2 :     Real64 sourceSideInitTemp = 20;
     725           2 :     Real64 sourceSideHumRat = 0.0;
     726           2 :     if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
     727             :         // same here; update later
     728           1 :         sourceSideInitTemp = 20;
     729             :     }
     730             : 
     731           2 :     Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
     732           2 :     Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
     733             : 
     734             :     // set the source-side flow rate
     735           2 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
     736             :         // load-side capacity should already be set, so unless the flow rate is specified, we can set
     737             :         // an assumed reasonable flow rate since this doesn't affect downstream components
     738           0 :         Real64 DeltaT_src = 10;
     739             :         // to get the source flow, we first must calculate the required heat impact on the source side
     740             :         // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
     741             :         // Then the energy balance:     Qsrc = Qload + Power
     742             :         // Substituting for Power:      Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
     743           0 :         Real64 const designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
     744             :         // To get the design source flow rate, just apply the sensible heat rate equation:
     745             :         //                              Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
     746             :         //                              Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
     747           0 :         tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
     748           2 :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0) {
     749             :         // given the value by the user
     750             :         // set it directly
     751           2 :         tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
     752             :     } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0) { // LCOV_EXCL_LINE
     753             :         // user gave a flow rate of 0
     754             :         // protected by the input processor to be >0.0
     755             :         // fatal out just in case
     756             :         errorsFound = true; // LCOV_EXCL_LINE
     757           0 :         ShowSevereError(state,
     758           0 :                         format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
     759             :                                this->name,
     760             :                                this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
     761             :     } else {
     762             :         // can't imagine how it would ever get to this point
     763             :         // just assume it's the same as the load side if we don't have any sizing information
     764             :         tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
     765             :     }
     766             : 
     767           2 :     this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
     768             : 
     769           2 :     std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
     770           2 :     if (this->sourceSideDesignVolFlowRateWasAutoSized) {
     771           0 :         this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
     772           0 :         if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     773           0 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
     774             :         }
     775           0 :         if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     776           0 :             BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
     777             :         }
     778             :     } else {
     779             :         // source design flow was hard-sized
     780           2 :         if (this->sourceSideDesignVolFlowRate > 0.0) {
     781           2 :             if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     782           2 :                 if (state.dataGlobal->DoPlantSizing) {
     783           0 :                     BaseSizer::reportSizerOutput(state,
     784             :                                                  typeName,
     785             :                                                  this->name,
     786             :                                                  "Design Size Source Side Volume Flow Rate [m3/s]",
     787             :                                                  tmpSourceVolFlow,
     788             :                                                  "User-Specified Source Side Volume Flow Rate [m3/s]",
     789           0 :                                                  this->sourceSideDesignVolFlowRate);
     790             :                 } else {
     791           4 :                     BaseSizer::reportSizerOutput(
     792           2 :                         state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", this->sourceSideDesignVolFlowRate);
     793             :                 }
     794             :             }
     795             :         }
     796             :     }
     797             : 
     798           2 :     if (errorsFound) {
     799             :         ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
     800             :     }
     801           2 : }
     802             : 
     803           6 : PlantComponent *EIRPlantLoopHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type_of_num, const std::string &hp_name)
     804             : {
     805           6 :     if (state.dataEIRPlantLoopHeatPump->getInputsPLHP) {
     806           2 :         EIRPlantLoopHeatPump::processInputForEIRPLHP(state);
     807           2 :         EIRPlantLoopHeatPump::pairUpCompanionCoils(state);
     808           2 :         state.dataEIRPlantLoopHeatPump->getInputsPLHP = false;
     809             :     }
     810             : 
     811           9 :     for (auto &plhp : state.dataEIRPlantLoopHeatPump->heatPumps) {
     812           9 :         if (plhp.name == UtilityRoutines::MakeUPPERCase(hp_name) && plhp.EIRHPType == hp_type_of_num) {
     813           6 :             return &plhp;
     814             :         }
     815             :     }
     816             : 
     817           0 :     ShowFatalError(state, "EIR Plant Loop Heat Pump factory: Error getting inputs for PLHP named: " + hp_name);
     818             :     return nullptr; // LCOV_EXCL_LINE
     819             : }
     820             : 
     821           2 : void EIRPlantLoopHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
     822             : {
     823           6 :     for (auto &thisHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
     824           4 :         if (!thisHP.companionCoilName.empty()) {
     825           8 :             auto thisCoilName = UtilityRoutines::MakeUPPERCase(thisHP.name);
     826           4 :             auto &thisCoilType = thisHP.EIRHPType;
     827           8 :             auto targetCompanionName = UtilityRoutines::MakeUPPERCase(thisHP.companionCoilName);
     828           6 :             for (auto &potentialCompanionCoil : state.dataEIRPlantLoopHeatPump->heatPumps) {
     829           6 :                 auto &potentialCompanionType = potentialCompanionCoil.EIRHPType;
     830           6 :                 auto potentialCompanionName = UtilityRoutines::MakeUPPERCase(potentialCompanionCoil.name);
     831           6 :                 if (potentialCompanionName == thisCoilName) {
     832             :                     // skip the current coil
     833           2 :                     continue;
     834             :                 }
     835           4 :                 if (potentialCompanionName == targetCompanionName) {
     836           4 :                     if (thisCoilType == potentialCompanionType) {
     837           0 :                         ShowSevereError(state, "Invalid companion specification for EIR Plant Loop Heat Pump named \"" + thisCoilName + "\"");
     838           0 :                         ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
     839           0 :                         ShowFatalError(state, "Invalid companion object causes program termination");
     840             :                     }
     841           4 :                     thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
     842           4 :                     break;
     843             :                 }
     844             :             }
     845           4 :             if (!thisHP.companionHeatPumpCoil) {
     846           0 :                 ShowSevereError(state, "Could not find matching companion heat pump coil.");
     847           0 :                 ShowContinueError(state, "Base coil: " + thisCoilName);
     848           0 :                 ShowContinueError(state, "Looking for companion coil named: " + targetCompanionName);
     849           0 :                 ShowFatalError(state, "Simulation aborts due to previous severe error");
     850             :             }
     851             :         }
     852             :     }
     853           2 : }
     854             : 
     855           2 : void EIRPlantLoopHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
     856             : {
     857             : 
     858          12 :     struct ClassType
     859             :     {
     860             :         DataPlant::PlantEquipmentType thisType;
     861             :         std::string nodesType;
     862             :         std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
     863             :         std::function<Real64(Real64, Real64)> calcQsource;
     864             :         std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
     865             : 
     866           4 :         ClassType(DataPlant::PlantEquipmentType _thisType,
     867             :                   std::string _nodesType,
     868             :                   std::function<Real64(Real64, Real64)> _tLoadOutFunc,
     869             :                   std::function<Real64(Real64, Real64)> _qSrcFunc,
     870             :                   std::function<Real64(Real64, Real64)> _tSrcOutFunc)
     871          16 :             : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(std::move(_tLoadOutFunc)), calcQsource(std::move(_qSrcFunc)),
     872          16 :               calcSourceOutletTemp(std::move(_tSrcOutFunc))
     873             :         {
     874           4 :         }
     875             :     };
     876             :     std::vector<ClassType> classesToInput = {ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRCooling,
     877             :                                                        "Chilled Water Nodes",
     878             :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
     879             :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
     880             :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add},
     881             :                                              ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRHeating,
     882             :                                                        "Hot Water Nodes",
     883             :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
     884             :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
     885           4 :                                                        EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract}};
     886             : 
     887           2 :     bool errorsFound = false;
     888           2 :     auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
     889           6 :     for (auto &classToInput : classesToInput) {
     890           4 :         cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
     891           8 :         auto objType = (DataLoopNode::ConnectionObjectType)getEnumerationValue(BranchNodeConnections::ConnectionObjectTypeNamesUC,
     892          12 :                                                                                UtilityRoutines::MakeUPPERCase(cCurrentModuleObject));
     893           4 :         int numPLHP = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
     894           4 :         if (numPLHP > 0) {
     895           8 :             auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
     896           4 :             if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
     897             :                 // Cannot imagine how you would have numPLHP > 0 and yet the instances is empty
     898             :                 // this would indicate a major problem in the input processor, not a problem here
     899             :                 // I'll still catch this with errorsFound but I cannot make a unit test for it so excluding the line from coverage
     900             :                 ShowSevereError(state,                                                                   // LCOV_EXCL_LINE
     901             :                                 "EIR PLHP: Somehow getNumObjectsFound was > 0 but epJSON.find found 0"); // LCOV_EXCL_LINE
     902             :                 errorsFound = true;                                                                      // LCOV_EXCL_LINE
     903             :             }
     904           4 :             auto &instancesValue = instances.value();
     905           8 :             for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
     906           4 :                 auto const &fields = instance.value();
     907           4 :                 auto const &thisObjectName = instance.key();
     908           4 :                 state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
     909             : 
     910           8 :                 EIRPlantLoopHeatPump thisPLHP;
     911           4 :                 thisPLHP.EIRHPType = classToInput.thisType;
     912           4 :                 thisPLHP.name = UtilityRoutines::MakeUPPERCase(thisObjectName);
     913           8 :                 std::string loadSideInletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("load_side_inlet_node_name").get<std::string>());
     914           8 :                 std::string loadSideOutletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("load_side_outlet_node_name").get<std::string>());
     915           8 :                 std::string condenserType = UtilityRoutines::MakeUPPERCase(fields.at("condenser_type").get<std::string>());
     916           8 :                 std::string sourceSideInletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("source_side_inlet_node_name").get<std::string>());
     917           8 :                 std::string sourceSideOutletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("source_side_outlet_node_name").get<std::string>());
     918           4 :                 if (fields.find("companion_heat_pump_name") != fields.end()) { // optional field
     919           4 :                     thisPLHP.companionCoilName = UtilityRoutines::MakeUPPERCase(fields.at("companion_heat_pump_name").get<std::string>());
     920             :                 }
     921           8 :                 auto tmpFlowRate = fields.at("load_side_reference_flow_rate");
     922           4 :                 if (tmpFlowRate == "Autosize") {
     923           0 :                     thisPLHP.loadSideDesignVolFlowRate = DataSizing::AutoSize;
     924           0 :                     thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
     925             :                 } else {
     926           4 :                     thisPLHP.loadSideDesignVolFlowRate = tmpFlowRate.get<Real64>();
     927             :                 }
     928           8 :                 auto tmpSourceFlowRate = fields.at("source_side_reference_flow_rate");
     929           4 :                 if (tmpSourceFlowRate == "Autosize") {
     930           0 :                     thisPLHP.sourceSideDesignVolFlowRate = DataSizing::AutoSize;
     931           0 :                     thisPLHP.sourceSideDesignVolFlowRateWasAutoSized = true;
     932             :                 } else {
     933           4 :                     thisPLHP.sourceSideDesignVolFlowRate = tmpSourceFlowRate.get<Real64>();
     934             :                 }
     935           8 :                 auto tmpRefCapacity = fields.at("reference_capacity");
     936           4 :                 if (tmpRefCapacity == "Autosize") {
     937           0 :                     thisPLHP.referenceCapacity = DataSizing::AutoSize;
     938           0 :                     thisPLHP.referenceCapacityWasAutoSized = true;
     939             :                 } else {
     940           4 :                     thisPLHP.referenceCapacity = tmpRefCapacity.get<Real64>();
     941             :                 }
     942             : 
     943           4 :                 if (fields.find("reference_coefficient_of_performance") != fields.end()) {
     944           4 :                     thisPLHP.referenceCOP = fields.at("reference_coefficient_of_performance").get<Real64>();
     945             :                 } else {
     946           0 :                     Real64 defaultVal = 0.0;
     947           0 :                     if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
     948             :                             state, cCurrentModuleObject, "reference_coefficient_of_performance", defaultVal)) {
     949             :                         // this error condition would mean that someone broke the input dictionary, not their
     950             :                         // input file.  I can't really unit test it so I'll leave it here as a severe error
     951             :                         // but excluding it from coverage
     952             :                         ShowSevereError(state,                                                                  // LCOV_EXCL_LINE
     953             :                                         "EIR PLHP: Reference COP not entered and could not get default value"); // LCOV_EXCL_LINE
     954             :                         errorsFound = true;                                                                     // LCOV_EXCL_LINE
     955             :                     } else {
     956           0 :                         thisPLHP.referenceCOP = defaultVal;
     957             :                     }
     958             :                 }
     959             : 
     960           4 :                 if (fields.find("sizing_factor") != fields.end()) {
     961           0 :                     thisPLHP.sizingFactor = fields.at("sizing_factor").get<Real64>();
     962             :                 } else {
     963           4 :                     Real64 defaultVal = 0.0;
     964           4 :                     if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "sizing_factor", defaultVal)) {
     965             :                         // this error condition would mean that someone broke the input dictionary, not their
     966             :                         // input file.  I can't really unit test it so I'll leave it here as a severe error
     967             :                         // but excluding it from coverage
     968             :                         ShowSevereError(state,                                                                  // LCOV_EXCL_LINE
     969             :                                         "EIR PLHP: Sizing factor not entered and could not get default value"); // LCOV_EXCL_LINE
     970             :                         errorsFound = true;                                                                     // LCOV_EXCL_LINE
     971             :                     } else {
     972           4 :                         thisPLHP.sizingFactor = defaultVal;
     973             :                     }
     974             :                 }
     975             : 
     976           4 :                 auto &capFtName = fields.at("capacity_modifier_function_of_temperature_curve_name");
     977           4 :                 thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(capFtName.get<std::string>()));
     978           4 :                 if (thisPLHP.capFuncTempCurveIndex == 0) {
     979           0 :                     ShowSevereError(
     980           0 :                         state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + capFtName.get<std::string>());
     981           0 :                     errorsFound = true;
     982             :                 }
     983           4 :                 auto &eirFtName = fields.at("electric_input_to_output_ratio_modifier_function_of_temperature_curve_name");
     984           4 :                 thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(eirFtName.get<std::string>()));
     985           4 :                 if (thisPLHP.capFuncTempCurveIndex == 0) {
     986           0 :                     ShowSevereError(
     987           0 :                         state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + eirFtName.get<std::string>());
     988           0 :                     errorsFound = true;
     989             :                 }
     990           4 :                 auto &eirFplrName = fields.at("electric_input_to_output_ratio_modifier_function_of_part_load_ratio_curve_name");
     991           4 :                 thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(eirFplrName.get<std::string>()));
     992           4 :                 if (thisPLHP.capFuncTempCurveIndex == 0) {
     993           0 :                     ShowSevereError(
     994           0 :                         state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + eirFplrName.get<std::string>());
     995           0 :                     errorsFound = true;
     996             :                 }
     997             : 
     998           4 :                 bool nodeErrorsFound = false;
     999           4 :                 thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    1000             :                                                                                    loadSideInletNodeName,
    1001             :                                                                                    nodeErrorsFound,
    1002             :                                                                                    objType,
    1003             :                                                                                    thisPLHP.name,
    1004             :                                                                                    DataLoopNode::NodeFluidType::Water,
    1005             :                                                                                    DataLoopNode::ConnectionType::Inlet,
    1006             :                                                                                    NodeInputManager::CompFluidStream::Primary,
    1007           4 :                                                                                    DataLoopNode::ObjectIsNotParent);
    1008           4 :                 thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    1009             :                                                                                     loadSideOutletNodeName,
    1010             :                                                                                     nodeErrorsFound,
    1011             :                                                                                     objType,
    1012             :                                                                                     thisPLHP.name,
    1013             :                                                                                     DataLoopNode::NodeFluidType::Water,
    1014             :                                                                                     DataLoopNode::ConnectionType::Outlet,
    1015             :                                                                                     NodeInputManager::CompFluidStream::Primary,
    1016           4 :                                                                                     DataLoopNode::ObjectIsNotParent);
    1017           4 :                 DataLoopNode::NodeFluidType condenserNodeType = DataLoopNode::NodeFluidType::Blank;
    1018           4 :                 DataLoopNode::ConnectionType condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Blank;
    1019           4 :                 DataLoopNode::ConnectionType condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Blank;
    1020           4 :                 if (condenserType == "WATERSOURCE") {
    1021           2 :                     thisPLHP.waterSource = true;
    1022           2 :                     condenserNodeType = DataLoopNode::NodeFluidType::Water;
    1023           2 :                     condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
    1024           2 :                     condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
    1025           2 :                 } else if (condenserType == "AIRSOURCE") {
    1026           2 :                     thisPLHP.airSource = true;
    1027           2 :                     condenserNodeType = DataLoopNode::NodeFluidType::Air;
    1028           2 :                     condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::OutsideAir;
    1029           2 :                     condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::OutsideAir;
    1030             :                 } else {
    1031             :                     // Again, this should be protected by the input processor
    1032           0 :                     ShowErrorMessage(state,
    1033             :                                      "Invalid heat pump condenser type (name=" + thisPLHP.name + // LCOV_EXCL_LINE
    1034             :                                          "; entered type: " + condenserType);                    // LCOV_EXCL_LINE
    1035             :                     errorsFound = true;                                                          // LCOV_EXCL_LINE
    1036             :                 }
    1037           4 :                 thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
    1038             :                                                                                      sourceSideInletNodeName,
    1039             :                                                                                      nodeErrorsFound,
    1040             :                                                                                      objType,
    1041             :                                                                                      thisPLHP.name,
    1042             :                                                                                      condenserNodeType,
    1043             :                                                                                      condenserNodeConnectionType_Inlet,
    1044             :                                                                                      NodeInputManager::CompFluidStream::Secondary,
    1045           4 :                                                                                      DataLoopNode::ObjectIsNotParent);
    1046           4 :                 thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
    1047             :                                                                                       sourceSideOutletNodeName,
    1048             :                                                                                       nodeErrorsFound,
    1049             :                                                                                       objType,
    1050             :                                                                                       thisPLHP.name,
    1051             :                                                                                       condenserNodeType,
    1052             :                                                                                       condenserNodeConnectionType_Outlet,
    1053             :                                                                                       NodeInputManager::CompFluidStream::Secondary,
    1054           4 :                                                                                       DataLoopNode::ObjectIsNotParent);
    1055           4 :                 if (nodeErrorsFound) errorsFound = true;
    1056           4 :                 BranchNodeConnections::TestCompSet(
    1057             :                     state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
    1058             : 
    1059           4 :                 if (thisPLHP.waterSource) {
    1060           2 :                     BranchNodeConnections::TestCompSet(
    1061             :                         state, cCurrentModuleObject, thisPLHP.name, sourceSideInletNodeName, sourceSideOutletNodeName, "Condenser Water Nodes");
    1062             :                 }
    1063             : 
    1064             :                 // store the worker functions that generalized the heating/cooling sides
    1065           4 :                 thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
    1066           4 :                 thisPLHP.calcQsource = classToInput.calcQsource;
    1067           4 :                 thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
    1068             : 
    1069           4 :                 if (!errorsFound) {
    1070           4 :                     state.dataEIRPlantLoopHeatPump->heatPumps.push_back(thisPLHP);
    1071             :                 }
    1072             :             }
    1073             :         }
    1074             :     }
    1075           2 :     if (errorsFound) {
    1076             :         // currently there are no straightforward unit tests possible to get here
    1077             :         // all curves are required and inputs are validated by the input processor
    1078             :         // obviously this will stay here but I don't feel like counting it against coverage
    1079             :         ShowFatalError(state, "Previous EIR PLHP errors cause program termination"); // LCOV_EXCL_LINE
    1080             :     }
    1081           2 : }
    1082             : 
    1083      523087 : void EIRPlantLoopHeatPump::checkConcurrentOperation(EnergyPlusData &state)
    1084             : {
    1085             :     // This will do a recurring warning for concurrent companion operation.
    1086             :     // This function should be called at the end of the time-step to ensure any iteration-level operation
    1087             :     //  is worked out and the results are final.
    1088             :     // This function does not try to be intelligent about only reporting for one of the companions.  The only
    1089             :     //  way I could think of was to have a vector, either static here or in the namespace, that would hold
    1090             :     //  companion index values as I warn against their partner, so then I would have to add the values to the
    1091             :     //  vector each pass, and check then each loop.  This seemed really bulky and inefficient, so I chose to
    1092             :     //  leave a tight loop here of just reporting for each coil if it and the companion are running.
    1093      523279 :     for (auto &thisPLHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
    1094         192 :         if (!thisPLHP.companionHeatPumpCoil) {
    1095           0 :             continue;
    1096             :         }
    1097         192 :         if (thisPLHP.running && thisPLHP.companionHeatPumpCoil->running) {
    1098           0 :             ShowRecurringWarningErrorAtEnd(state,
    1099           0 :                                            "Companion heat pump objects running concurrently, check operation.  Base object name: " + thisPLHP.name,
    1100             :                                            thisPLHP.recurringConcurrentOperationWarningIndex);
    1101             :         }
    1102             :     }
    1103      523087 : }
    1104          30 : void EIRPlantLoopHeatPump::oneTimeInit(EnergyPlusData &state)
    1105             : {
    1106             :     // This function does all the one-time initialization
    1107          30 :     std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
    1108             : 
    1109          30 :     if (this->oneTimeInitFlag) {
    1110           4 :         bool errFlag = false;
    1111             : 
    1112             :         // setup output variables
    1113           8 :         SetupOutputVariable(state,
    1114             :                             "Heat Pump Load Side Heat Transfer Rate",
    1115             :                             OutputProcessor::Unit::W,
    1116             :                             this->loadSideHeatTransfer,
    1117             :                             OutputProcessor::SOVTimeStepType::System,
    1118             :                             OutputProcessor::SOVStoreType::Average,
    1119           4 :                             this->name);
    1120           8 :         SetupOutputVariable(state,
    1121             :                             "Heat Pump Load Side Heat Transfer Energy",
    1122             :                             OutputProcessor::Unit::J,
    1123             :                             this->loadSideEnergy,
    1124             :                             OutputProcessor::SOVTimeStepType::System,
    1125             :                             OutputProcessor::SOVStoreType::Summed,
    1126             :                             this->name,
    1127             :                             _,
    1128             :                             "ENERGYTRANSFER",
    1129             :                             _,
    1130             :                             _,
    1131           4 :                             "Plant");
    1132           8 :         SetupOutputVariable(state,
    1133             :                             "Heat Pump Source Side Heat Transfer Rate",
    1134             :                             OutputProcessor::Unit::W,
    1135             :                             this->sourceSideHeatTransfer,
    1136             :                             OutputProcessor::SOVTimeStepType::System,
    1137             :                             OutputProcessor::SOVStoreType::Average,
    1138           4 :                             this->name);
    1139           8 :         SetupOutputVariable(state,
    1140             :                             "Heat Pump Source Side Heat Transfer Energy",
    1141             :                             OutputProcessor::Unit::J,
    1142             :                             this->sourceSideEnergy,
    1143             :                             OutputProcessor::SOVTimeStepType::System,
    1144             :                             OutputProcessor::SOVStoreType::Summed,
    1145           4 :                             this->name);
    1146           8 :         SetupOutputVariable(state,
    1147             :                             "Heat Pump Load Side Inlet Temperature",
    1148             :                             OutputProcessor::Unit::C,
    1149             :                             this->loadSideInletTemp,
    1150             :                             OutputProcessor::SOVTimeStepType::System,
    1151             :                             OutputProcessor::SOVStoreType::Average,
    1152           4 :                             this->name);
    1153           8 :         SetupOutputVariable(state,
    1154             :                             "Heat Pump Load Side Outlet Temperature",
    1155             :                             OutputProcessor::Unit::C,
    1156             :                             this->loadSideOutletTemp,
    1157             :                             OutputProcessor::SOVTimeStepType::System,
    1158             :                             OutputProcessor::SOVStoreType::Average,
    1159           4 :                             this->name);
    1160           8 :         SetupOutputVariable(state,
    1161             :                             "Heat Pump Source Side Inlet Temperature",
    1162             :                             OutputProcessor::Unit::C,
    1163             :                             this->sourceSideInletTemp,
    1164             :                             OutputProcessor::SOVTimeStepType::System,
    1165             :                             OutputProcessor::SOVStoreType::Average,
    1166           4 :                             this->name);
    1167           8 :         SetupOutputVariable(state,
    1168             :                             "Heat Pump Source Side Outlet Temperature",
    1169             :                             OutputProcessor::Unit::C,
    1170             :                             this->sourceSideOutletTemp,
    1171             :                             OutputProcessor::SOVTimeStepType::System,
    1172             :                             OutputProcessor::SOVStoreType::Average,
    1173           4 :                             this->name);
    1174           8 :         SetupOutputVariable(state,
    1175             :                             "Heat Pump Electricity Rate",
    1176             :                             OutputProcessor::Unit::W,
    1177             :                             this->powerUsage,
    1178             :                             OutputProcessor::SOVTimeStepType::System,
    1179             :                             OutputProcessor::SOVStoreType::Average,
    1180           4 :                             this->name);
    1181           4 :         if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) { // energy from HeatPump:PlantLoop:EIR:Cooling object
    1182           4 :             SetupOutputVariable(state,
    1183             :                                 "Heat Pump Electricity Energy",
    1184             :                                 OutputProcessor::Unit::J,
    1185             :                                 this->powerEnergy,
    1186             :                                 OutputProcessor::SOVTimeStepType::System,
    1187             :                                 OutputProcessor::SOVStoreType::Summed,
    1188             :                                 this->name,
    1189             :                                 _,
    1190             :                                 "Electricity",
    1191             :                                 "Cooling",
    1192             :                                 "Heat Pump",
    1193           2 :                                 "Plant");
    1194           2 :         } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // energy from HeatPump:PlantLoop:EIR:Heating object
    1195           4 :             SetupOutputVariable(state,
    1196             :                                 "Heat Pump Electricity Energy",
    1197             :                                 OutputProcessor::Unit::J,
    1198             :                                 this->powerEnergy,
    1199             :                                 OutputProcessor::SOVTimeStepType::System,
    1200             :                                 OutputProcessor::SOVStoreType::Summed,
    1201             :                                 this->name,
    1202             :                                 _,
    1203             :                                 "Electricity",
    1204             :                                 "Heating",
    1205             :                                 "Heat Pump",
    1206           2 :                                 "Plant");
    1207             :         }
    1208           8 :         SetupOutputVariable(state,
    1209             :                             "Heat Pump Load Side Mass Flow Rate",
    1210             :                             OutputProcessor::Unit::kg_s,
    1211             :                             this->loadSideMassFlowRate,
    1212             :                             OutputProcessor::SOVTimeStepType::System,
    1213             :                             OutputProcessor::SOVStoreType::Average,
    1214           4 :                             this->name);
    1215           8 :         SetupOutputVariable(state,
    1216             :                             "Heat Pump Source Side Mass Flow Rate",
    1217             :                             OutputProcessor::Unit::kg_s,
    1218             :                             this->sourceSideMassFlowRate,
    1219             :                             OutputProcessor::SOVTimeStepType::System,
    1220             :                             OutputProcessor::SOVStoreType::Average,
    1221           4 :                             this->name);
    1222             : 
    1223             :         // find this component on the plant
    1224           4 :         bool thisErrFlag = false;
    1225           4 :         PlantUtilities::ScanPlantLoopsForObject(
    1226             :             state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
    1227             : 
    1228           4 :         if (thisErrFlag) {
    1229           0 :             ShowSevereError(state,
    1230           0 :                             format("{}: Plant topology problem for {} name = \"{}\"",
    1231             :                                    routineName,
    1232           0 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    1233           0 :                                    this->name));
    1234           0 :             ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
    1235           0 :             errFlag = true;
    1236           4 :         } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
    1237           0 :             ShowSevereError(state,
    1238           0 :                             format("{}: Invalid connections for {} name = \"{}\"",
    1239             :                                    routineName,
    1240           0 :                                    DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    1241           0 :                                    this->name));
    1242           0 :             ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
    1243           0 :             errFlag = true;
    1244             :         }
    1245             : 
    1246           4 :         thisErrFlag = false;
    1247           4 :         if (this->waterSource) {
    1248           2 :             PlantUtilities::ScanPlantLoopsForObject(
    1249             :                 state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
    1250             : 
    1251           2 :             if (thisErrFlag) {
    1252           0 :                 ShowSevereError(state,
    1253           0 :                                 format("{}: Plant topology problem for {} name = \"{}\"",
    1254             :                                        routineName,
    1255           0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    1256           0 :                                        this->name));
    1257           0 :                 ShowContinueError(state, "Could not locate component's source side connections on a plant loop");
    1258           0 :                 errFlag = true;
    1259           2 :             } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
    1260           0 :                 ShowSevereError(state,
    1261           0 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    1262             :                                        routineName,
    1263           0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    1264           0 :                                        this->name));
    1265           0 :                 ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop");
    1266           0 :                 errFlag = true;
    1267             :             }
    1268             : 
    1269             :             // make sure it is not the same loop on both sides.
    1270           2 :             if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
    1271           0 :                 ShowSevereError(state,
    1272           0 :                                 format("{}: Invalid connections for {} name = \"{}\"",
    1273             :                                        routineName,
    1274           0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
    1275           0 :                                        this->name));
    1276           0 :                 ShowContinueError(state, "The load and source sides need to be on different loops.");
    1277           0 :                 errFlag = true;
    1278             :             } else {
    1279             : 
    1280           2 :                 PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
    1281             :             }
    1282           2 :         } else if (this->airSource) {
    1283             :             // nothing to do here ?
    1284             :         }
    1285             : 
    1286           4 :         if (errFlag) {
    1287           0 :             ShowFatalError(state, routineName + ": Program terminated due to previous condition(s).");
    1288             :         }
    1289           4 :         this->oneTimeInitFlag = false;
    1290             :     }
    1291          30 : }
    1292        2313 : } // namespace EnergyPlus::EIRPlantLoopHeatPumps

Generated by: LCOV version 1.13