LCOV - code coverage report
Current view: top level - EnergyPlus - PlantUtilities.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 589 830 71.0 %
Date: 2024-08-24 18:31:18 Functions: 29 31 93.5 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Array.functions.hh>
      53             : #include <ObjexxFCL/Array1D.hh>
      54             : #include <ObjexxFCL/Fmath.hh>
      55             : 
      56             : // EnergyPlus Headers
      57             : #include <EnergyPlus/BranchInputManager.hh>
      58             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59             : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
      60             : #include <EnergyPlus/DataLoopNode.hh>
      61             : #include <EnergyPlus/DataSizing.hh>
      62             : #include <EnergyPlus/FluidProperties.hh>
      63             : #include <EnergyPlus/Plant/DataPlant.hh>
      64             : #include <EnergyPlus/PlantUtilities.hh>
      65             : #include <EnergyPlus/Pumps.hh>
      66             : #include <EnergyPlus/UtilityRoutines.hh>
      67             : 
      68             : namespace EnergyPlus::PlantUtilities {
      69             : 
      70             : // Module containing the routines dealing with the <module_name>
      71             : 
      72             : // MODULE INFORMATION:
      73             : //       AUTHOR         <author>
      74             : //       DATE WRITTEN   <date_written>
      75             : //       MODIFIED       na
      76             : //       RE-ENGINEERED  na
      77             : 
      78      168657 : void InitComponentNodes(EnergyPlusData &state, Real64 const MinCompMdot, Real64 const MaxCompMdot, int const InletNode, int const OutletNode)
      79             : {
      80             : 
      81             :     // SUBROUTINE INFORMATION:
      82             :     //       AUTHOR         Brent Griffith
      83             :     //       DATE WRITTEN   Sept 2010
      84             :     //       MODIFIED       na
      85             :     //       RE-ENGINEERED  na
      86             : 
      87             :     // PURPOSE OF THIS SUBROUTINE:
      88             :     //  Central routine for initializing plant nodes connected to components
      89             :     //  typically used for BeginEnvrnFlag
      90             : 
      91             :     // METHODOLOGY EMPLOYED:
      92             :     // set MassFlowRate variables on inlet node
      93             :     //  reset inlet node if more restrictive
      94             : 
      95             :     // Using/Aliasing
      96             : 
      97             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
      98             :     Real64 tmpMinCompMdot; // local value
      99             :     Real64 tmpMaxCompMdot; // local value
     100             : 
     101      168657 :     tmpMinCompMdot = MinCompMdot;
     102      168657 :     tmpMaxCompMdot = MaxCompMdot;
     103             :     // trap bad values that can happen before all the setup is done
     104      168657 :     if (tmpMinCompMdot < 0.0) tmpMinCompMdot = 0.0;
     105      168657 :     if (tmpMaxCompMdot < 0.0) tmpMaxCompMdot = 0.0;
     106             : 
     107             :     // reset outlet node
     108      168657 :     state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
     109             : 
     110      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRateMin = tmpMinCompMdot;
     111      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = tmpMinCompMdot;
     112      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRateMax = tmpMaxCompMdot;
     113      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = tmpMaxCompMdot;
     114             :     // reset inlet node, but only change from inlet setting if set and more restrictive
     115      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
     116      168657 :     state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = 0.0;
     117      168657 : }
     118             : 
     119   252642176 : void SetComponentFlowRate(EnergyPlusData &state,
     120             :                           Real64 &CompFlow,             // [kg/s]
     121             :                           int const InletNode,          // component's inlet node index in node structure
     122             :                           int const OutletNode,         // component's outlet node index in node structure
     123             :                           PlantLocation const &plantLoc // component location for PlantLoop
     124             : )
     125             : {
     126             : 
     127             :     // SUBROUTINE INFORMATION:
     128             :     //       AUTHOR         Dan Fisher
     129             :     //       DATE WRITTEN   August 2009
     130             :     //       MODIFIED       na
     131             :     //       RE-ENGINEERED  na
     132             : 
     133             :     // PURPOSE OF THIS SUBROUTINE:
     134             :     // General purpose worker routine to set flows for a component model
     135             : 
     136   252642176 :     if (plantLoc.loopNum == 0) { // protect from hard crash below
     137           0 :         if (InletNode > 0) {
     138           0 :             ShowSevereError(state,
     139           0 :                             format("SetComponentFlowRate: trapped plant loop index = 0, check component with inlet node named={}",
     140           0 :                                    state.dataLoopNodes->NodeID(InletNode)));
     141             :         } else {
     142           0 :             ShowSevereError(state, "SetComponentFlowRate: trapped plant loop node id = 0");
     143             :         }
     144           0 :         return;
     145             :         // this crashes during ManageSizing, maybe it's just an init thing...
     146             :         // ShowFatalError(state, "Preceding loop index error causes program termination");
     147             :     }
     148             : 
     149   252642176 :     Real64 const MdotOldRequest = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
     150   252642176 :     auto &loop_side = state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum);
     151   252642176 :     auto &comp = loop_side.Branch(plantLoc.branchNum).Comp(plantLoc.compNum);
     152             : 
     153   252642176 :     if (comp.CurOpSchemeType == DataPlant::OpScheme::Demand) {
     154             :         // store flow request on inlet node
     155   151944143 :         state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
     156   151944143 :         state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
     157   151944143 :             max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
     158   151944143 :         state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
     159   151944143 :             min(state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMax);
     160             :         // virtual 2-way valve (was tried but it clamps down demand side component's flow options so they can't find proper solutions)
     161             :     } else {
     162             :         // lodge the original request for all types
     163   100698033 :         state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
     164             :     }
     165             : 
     166             :     // Update Min/Max Avail
     167             : 
     168   252642176 :     state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
     169   252642176 :         max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
     170   252642176 :     if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax >= 0.0) {
     171   252641681 :         state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
     172   252641681 :             min(state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMax);
     173             :     } else {
     174         495 :         if (!state.dataGlobal->SysSizingCalc && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     175             :             // throw error for developers, need to change a component model to set hardware limits on inlet
     176           0 :             if (!state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued) {
     177           0 :                 ShowSevereError(state,
     178           0 :                                 format("SetComponentFlowRate: check component model implementation for component with inlet node named={}",
     179           0 :                                        state.dataLoopNodes->NodeID(InletNode)));
     180           0 :                 ShowContinueError(state, format("Inlet node MassFlowRatMax = {:.8R}", state.dataLoopNodes->Node(InletNode).MassFlowRateMax));
     181           0 :                 state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued = true;
     182             :             }
     183             :         }
     184             :     }
     185             : 
     186             :     // Set loop flow rate
     187   252642176 :     if (loop_side.FlowLock == DataPlant::FlowLock::Unlocked) {
     188   164211494 :         if (state.dataPlnt->PlantLoop(plantLoc.loopNum).MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
     189       13656 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
     190       13656 :             state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     191             :         } else { // bound the flow by Min/Max available and hardware limits
     192   164197838 :             if (comp.FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
     193             :                 // determine highest flow request for all the components on the branch
     194     3483397 :                 Real64 SeriesBranchHighFlowRequest = 0.0;
     195     3483397 :                 Real64 SeriesBranchHardwareMaxLim = state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
     196     3483397 :                 Real64 SeriesBranchHardwareMinLim = 0.0;
     197     3483397 :                 Real64 SeriesBranchMaxAvail = state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail;
     198     3483397 :                 Real64 SeriesBranchMinAvail = 0.0;
     199             : 
     200             :                 // inserting EMS On/Off Supervisory control here to series branch constraint and assuming EMS should shut off flow completely
     201             :                 // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
     202     3483397 :                 bool EMSLoadOverride = false;
     203             : 
     204    10780555 :                 for (int CompNum = 1; CompNum <= loop_side.Branch(plantLoc.branchNum).TotalComponents; ++CompNum) {
     205     7297158 :                     auto &thisComp = loop_side.Branch(plantLoc.branchNum).Comp(CompNum);
     206     7297158 :                     int const CompInletNodeNum = thisComp.NodeNumIn;
     207     7297158 :                     auto &thisInletNode = state.dataLoopNodes->Node(CompInletNodeNum);
     208     7297158 :                     SeriesBranchHighFlowRequest = max(thisInletNode.MassFlowRateRequest, SeriesBranchHighFlowRequest);
     209     7297158 :                     SeriesBranchHardwareMaxLim = min(thisInletNode.MassFlowRateMax, SeriesBranchHardwareMaxLim);
     210     7297158 :                     SeriesBranchHardwareMinLim = max(thisInletNode.MassFlowRateMin, SeriesBranchHardwareMinLim);
     211     7297158 :                     SeriesBranchMaxAvail = min(thisInletNode.MassFlowRateMaxAvail, SeriesBranchMaxAvail);
     212     7297158 :                     SeriesBranchMinAvail = max(thisInletNode.MassFlowRateMinAvail, SeriesBranchMinAvail);
     213             :                     // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
     214     7297158 :                     if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
     215             :                 }
     216             : 
     217     3483397 :                 if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
     218      169121 :                     SeriesBranchHardwareMaxLim = 0.0;
     219             :                 }
     220             : 
     221             :                 // take higher of branch max flow request and this new flow request
     222     3483397 :                 CompFlow = max(CompFlow, SeriesBranchHighFlowRequest);
     223             : 
     224             :                 // apply constraints on component flow
     225     3483397 :                 CompFlow = max(CompFlow, SeriesBranchHardwareMinLim);
     226     3483397 :                 CompFlow = max(CompFlow, SeriesBranchMinAvail);
     227     3483397 :                 CompFlow = min(CompFlow, SeriesBranchHardwareMaxLim);
     228     3483397 :                 CompFlow = min(CompFlow, SeriesBranchMaxAvail);
     229             : 
     230     3483397 :                 if (CompFlow < DataBranchAirLoopPlant::MassFlowTolerance) CompFlow = 0.0;
     231     3483397 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
     232     3483397 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     233    10780555 :                 for (int CompNum = 1; CompNum <= loop_side.Branch(plantLoc.branchNum).TotalComponents; ++CompNum) {
     234     7297158 :                     auto &thisComp = loop_side.Branch(plantLoc.branchNum).Comp(CompNum);
     235     7297158 :                     int const CompInletNodeNum = thisComp.NodeNumIn;
     236     7297158 :                     int const CompOutletNodeNum = thisComp.NodeNumOut;
     237     7297158 :                     state.dataLoopNodes->Node(CompInletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     238     7297158 :                     state.dataLoopNodes->Node(CompOutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     239             :                 }
     240             : 
     241             :             } else { // not series active
     242   160714441 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate = max(state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail, CompFlow);
     243   160714441 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate =
     244   160714441 :                     max(state.dataLoopNodes->Node(InletNode).MassFlowRateMin, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
     245   160714441 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate =
     246   160714441 :                     min(state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
     247   160714441 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate =
     248   160714441 :                     min(state.dataLoopNodes->Node(InletNode).MassFlowRateMax, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
     249             : 
     250             :                 // inserting EMS On/Off Supervisory control here to override min constraint assuming EMS should shut off flow completely
     251             :                 // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
     252   160714441 :                 bool EMSLoadOverride = false;
     253             : 
     254   321630869 :                 for (int CompNum = 1; CompNum <= loop_side.Branch(plantLoc.branchNum).TotalComponents; ++CompNum) {
     255             :                     // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
     256   160916428 :                     auto &thisComp = loop_side.Branch(plantLoc.branchNum).Comp(CompNum);
     257   160916428 :                     if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
     258             :                 }
     259             : 
     260   160714441 :                 if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
     261           0 :                     state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
     262             :                 }
     263             : 
     264   160714441 :                 if (state.dataLoopNodes->Node(OutletNode).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance)
     265    75227713 :                     state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
     266   160714441 :                 CompFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     267   160714441 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     268             :             }
     269             :         }
     270    88430682 :     } else if (loop_side.FlowLock == DataPlant::FlowLock::Locked) {
     271    88430682 :         state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
     272    88430682 :         CompFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     273             :     } else {
     274             :         ShowFatalError(state, "SetComponentFlowRate: Flow lock out of range"); // DEBUG error...should never get here LCOV_EXCL_LINE
     275             :     }
     276             : 
     277   252642176 :     if (comp.CurOpSchemeType == DataPlant::OpScheme::Demand) {
     278   151944143 :         if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
     279    74592378 :             if (std::abs(MdotOldRequest - state.dataLoopNodes->Node(InletNode).MassFlowRateRequest) >
     280             :                 DataBranchAirLoopPlant::MassFlowTolerance) { // demand comp changed its flow request
     281    21474309 :                 loop_side.SimLoopSideNeeded = true;
     282             :             }
     283             :         }
     284             :     }
     285             : }
     286             : 
     287   185619345 : void SetActuatedBranchFlowRate(EnergyPlusData &state,
     288             :                                Real64 &CompFlow,
     289             :                                int const ActuatedNode,
     290             :                                PlantLocation const &plantLoc,
     291             :                                bool const ResetMode // flag to indicate if this is a real flow set, or a reset flow setting.
     292             : )
     293             : {
     294             : 
     295             :     // SUBROUTINE INFORMATION:
     296             :     //       AUTHOR         B. Griffith
     297             :     //       DATE WRITTEN   Feb 2010
     298             :     //       MODIFIED       na
     299             :     //       RE-ENGINEERED  na
     300             : 
     301             :     // PURPOSE OF THIS SUBROUTINE:
     302             :     // general purpse worker routine to set plant node variables for node
     303             :     // and all nodes on the branch.  Used by HVAC water coil controller, that do not
     304             :     //  distinguish single component and have no inlet-outlet pair
     305             :     //  only a actuated noded of no clear position.  set flow on entire branch
     306             : 
     307             :     // METHODOLOGY EMPLOYED:
     308             :     // Set flow on node and branch while honoring constraints on actuated node
     309             : 
     310   185619345 :     auto &a_node = state.dataLoopNodes->Node(ActuatedNode);
     311   185619345 :     if (plantLoc.loopNum == 0 || plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
     312             :         // early in simulation before plant loops are setup and found
     313           0 :         a_node.MassFlowRate = CompFlow;
     314           0 :         return;
     315             :     }
     316             : 
     317   185619345 :     auto &loop_side = state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum);
     318             : 
     319             :     // store original flow
     320   185619345 :     Real64 const MdotOldRequest = a_node.MassFlowRateRequest;
     321   185619345 :     a_node.MassFlowRateRequest = CompFlow;
     322   185619345 :     if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid && (!ResetMode)) {
     323   148931887 :         if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
     324   127228043 :             if ((std::abs(MdotOldRequest - a_node.MassFlowRateRequest) > DataBranchAirLoopPlant::MassFlowTolerance) &&
     325    53316029 :                 (loop_side.FlowLock == DataPlant::FlowLock::Unlocked)) {
     326    52488034 :                 loop_side.SimLoopSideNeeded = true;
     327             :             }
     328             :         }
     329             :     }
     330             :     // Set loop flow rate
     331             : 
     332   185619345 :     if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid) {
     333   185619345 :         auto const &branch = loop_side.Branch(plantLoc.branchNum);
     334   185619345 :         if (loop_side.FlowLock == DataPlant::FlowLock::Unlocked) {
     335   182866530 :             if (state.dataPlnt->PlantLoop(plantLoc.loopNum).MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
     336       21262 :                 a_node.MassFlowRate = CompFlow;
     337             :             } else { // bound the flow by Min/Max available across entire branch
     338             : 
     339   182845268 :                 a_node.MassFlowRate = max(a_node.MassFlowRateMinAvail, CompFlow);
     340   182845268 :                 a_node.MassFlowRate = max(a_node.MassFlowRateMin, a_node.MassFlowRate);
     341             :                 // add MassFlowRateMin hardware constraints
     342             : 
     343             :                 // inserting EMS On/Off Supervisory control here to override min constraint assuming EMS should shut off flow completely
     344             :                 // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
     345   182845268 :                 bool EMSLoadOverride = false;
     346             :                 // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
     347   365690536 :                 for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
     348   182845268 :                     auto const &comp = branch.Comp(CompNum);
     349   182845268 :                     if (comp.EMSLoadOverrideOn && comp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
     350             :                 }
     351   182845268 :                 if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
     352           0 :                     a_node.MassFlowRate = 0.0;
     353           0 :                     a_node.MassFlowRateRequest = 0.0;
     354             :                 }
     355             : 
     356   182845268 :                 a_node.MassFlowRate = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRate);
     357   182845268 :                 a_node.MassFlowRate = min(a_node.MassFlowRateMax, a_node.MassFlowRate);
     358   182845268 :                 if (a_node.MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) a_node.MassFlowRate = 0.0;
     359   365690536 :                 for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
     360   182845268 :                     auto const &comp = branch.Comp(CompNum);
     361   182845268 :                     if (ActuatedNode == comp.NodeNumIn) {
     362             :                         //            ! found controller set to inlet of a component.  now set that component's outlet
     363   182845268 :                         int const NodeNum = comp.NodeNumOut;
     364   182845268 :                         state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail = max(a_node.MassFlowRateMinAvail, a_node.MassFlowRateMin);
     365   182845268 :                         state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRateMax);
     366   182845268 :                         state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node.MassFlowRate;
     367             :                     }
     368             :                 }
     369             :             }
     370             : 
     371     2752815 :         } else if (loop_side.FlowLock == DataPlant::FlowLock::Locked) {
     372             : 
     373     2752815 :             CompFlow = a_node.MassFlowRate;
     374             :             // do not change requested flow rate either
     375     2752815 :             a_node.MassFlowRateRequest = MdotOldRequest;
     376     2752815 :             if ((CompFlow - a_node.MassFlowRateMaxAvail > DataBranchAirLoopPlant::MassFlowTolerance) ||
     377     2752815 :                 (a_node.MassFlowRateMinAvail - CompFlow > DataBranchAirLoopPlant::MassFlowTolerance)) {
     378           0 :                 ShowSevereError(state, "SetActuatedBranchFlowRate: Flow rate is out of range"); // DEBUG error...should never get here
     379           0 :                 ShowContinueErrorTimeStamp(state, "");
     380           0 :                 ShowContinueError(state, format("Component flow rate [kg/s] = {:.8R}", CompFlow));
     381           0 :                 ShowContinueError(state, format("Node maximum flow rate available [kg/s] = {:.8R}", a_node.MassFlowRateMaxAvail));
     382           0 :                 ShowContinueError(state, format("Node minimum flow rate available [kg/s] = {:.8R}", a_node.MassFlowRateMinAvail));
     383             :             }
     384             :         } else {
     385           0 :             ShowFatalError(state,
     386           0 :                            format("SetActuatedBranchFlowRate: Flowlock out of range, value={}",
     387             :                                   loop_side.FlowLock)); // DEBUG error...should never get here LCOV_EXCL_LINE
     388             :         }
     389             : 
     390   185619345 :         Real64 const a_node_MasFlowRate(a_node.MassFlowRate);
     391   185619345 :         Real64 const a_node_MasFlowRateRequest(a_node.MassFlowRateRequest);
     392   371238690 :         for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
     393   185619345 :             auto const &comp = branch.Comp(CompNum);
     394   185619345 :             int NodeNum = comp.NodeNumIn;
     395   185619345 :             state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
     396   185619345 :             state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
     397   185619345 :             NodeNum = comp.NodeNumOut;
     398   185619345 :             state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
     399   185619345 :             state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
     400             :         }
     401             :     }
     402             : }
     403             : 
     404     9511072 : Real64 RegulateCondenserCompFlowReqOp(EnergyPlusData &state, PlantLocation const &plantLoc, Real64 const TentativeFlowRequest)
     405             : {
     406             : 
     407             :     // FUNCTION INFORMATION:
     408             :     //       AUTHOR         Edwin Lee
     409             :     //       DATE WRITTEN   April 2012
     410             :     //       MODIFIED       na
     411             :     //       RE-ENGINEERED  na
     412             : 
     413             :     // PURPOSE OF THIS FUNCTION:
     414             :     // This function will do some intelligent flow request logic for condenser equipment.
     415             :     // Some condenser equipment (ground heat exchangers, etc.) may not have a meaningful load value
     416             :     //  since this is an environment heat transfer component.
     417             :     // The runflag is set, but may not be properly set, and the component may still request flow even
     418             :     //  when it doesn't need to.
     419             :     // This function will do a little more advanced logic than just checking runflag to determine whether
     420             :     //  to request any flow
     421             : 
     422             :     // METHODOLOGY EMPLOYED:
     423             :     // Query run flag and MyLoad
     424             :     // If run flag is OFF, then the component should actually be OFF, and tentative flow request will be zeroed
     425             :     // If the run flag is ON, then check the control type to determine if MyLoad is a meaningful value
     426             :     // If it is meaningful then determine whether to do flow request based on MyLoad
     427             :     // If not then we will have no choice but to leave the flow request alone (uncontrolled operation?)
     428             : 
     429             :     // Using/Aliasing
     430             : 
     431             :     // Return value
     432             :     Real64 FlowVal;
     433             : 
     434             :     // FUNCTION PARAMETER DEFINITIONS:
     435     9511072 :     Real64 constexpr ZeroLoad(0.0001);
     436             : 
     437             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     438             :     Real64 CompCurLoad;
     439             :     bool CompRunFlag;
     440             : 
     441     9511072 :     CompCurLoad = DataPlant::CompData::getPlantComponent(state, plantLoc).MyLoad;
     442     9511072 :     CompRunFlag = DataPlant::CompData::getPlantComponent(state, plantLoc).ON;
     443     9511072 :     DataPlant::OpScheme CompOpScheme = DataPlant::CompData::getPlantComponent(state, plantLoc).CurOpSchemeType;
     444             : 
     445     9511072 :     if (CompRunFlag) {
     446             : 
     447     4704413 :         switch (CompOpScheme) {
     448             : 
     449     3786367 :         case DataPlant::OpScheme::HeatingRB:
     450             :         case DataPlant::OpScheme::CoolingRB:
     451             :         case DataPlant::OpScheme::CompSetPtBased: { // These provide meaningful MyLoad values
     452     3786367 :             if (std::abs(CompCurLoad) > ZeroLoad) {
     453     3753439 :                 FlowVal = TentativeFlowRequest;
     454             :             } else { // no load
     455       32928 :                 FlowVal = 0.0;
     456             :             }
     457     3786367 :             break;
     458             :         }
     459      918046 :         default: { // Types that don't provide meaningful MyLoad values
     460      918046 :             FlowVal = TentativeFlowRequest;
     461      918046 :             break;
     462             :         }
     463             :         }
     464             : 
     465             :     } else { // runflag OFF
     466             : 
     467     4806659 :         FlowVal = 0.0;
     468             :     }
     469             : 
     470     9511072 :     return FlowVal;
     471             : }
     472             : 
     473     1438358 : bool AnyPlantSplitterMixerLacksContinuity(EnergyPlusData &state)
     474             : {
     475             : 
     476             :     // FUNCTION INFORMATION:
     477             :     //       AUTHOR         B. Griffith
     478             :     //       DATE WRITTEN   April 2012
     479             :     //       MODIFIED       na
     480             :     //       RE-ENGINEERED  na
     481             : 
     482             :     // PURPOSE OF THIS FUNCTION:
     483             :     // Similar to CheckPlantMixerSplitterConsistency, but used to decide if plant needs to iterate again
     484     5151468 :     for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
     485    11148488 :         for (DataPlant::LoopSideLocation LoopSide : DataPlant::LoopSideKeys) {
     486     7435378 :             if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.Exists) {
     487     7412180 :                 int const SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.NodeNumIn;
     488             :                 // loop across branch outlet nodes and check mass continuity
     489     7412180 :                 int const NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.TotalOutletNodes;
     490     7412180 :                 Real64 SumOutletFlow = 0.0;
     491    38468829 :                 for (int OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
     492    31056649 :                     int const BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.BranchNumOut(OutletNum);
     493    31056649 :                     int const LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Branch(BranchNum).NodeNumOut;
     494    31056649 :                     SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
     495             :                 }
     496     7412180 :                 Real64 const AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
     497     7412180 :                 if (AbsDifference > DataPlant::CriteriaDelta_MassFlowRate) {
     498        8064 :                     return true;
     499             :                 }
     500             :             }
     501             :         }
     502             :     }
     503     1430294 :     return false;
     504             : }
     505             : 
     506     7455662 : void CheckPlantMixerSplitterConsistency(EnergyPlusData &state,
     507             :                                         int const LoopNum,
     508             :                                         const DataPlant::LoopSideLocation LoopSideNum,
     509             :                                         bool const FirstHVACIteration)
     510             : {
     511             : 
     512             :     // SUBROUTINE INFORMATION:
     513             :     //       AUTHOR         Brent Griffith
     514             :     //       DATE WRITTEN   Oct 2007
     515             :     //       MODIFIED       na
     516             :     //       RE-ENGINEERED  na
     517             : 
     518             :     // PURPOSE OF THIS SUBROUTINE:
     519             :     // Check for plant flow resolver errors
     520             : 
     521             :     // METHODOLOGY EMPLOYED:
     522             :     // compare flow rate of splitter inlet to flow rate of mixer outlet
     523             : 
     524             :     // Using/Aliasing
     525             :     using DataPlant::CriteriaDelta_MassFlowRate;
     526             : 
     527             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     528             :     int MixerOutletNode;
     529             :     int SplitterInletNode;
     530             :     Real64 AbsDifference;
     531             :     int NumSplitterOutlets;
     532             :     Real64 SumOutletFlow;
     533             :     int OutletNum;
     534             :     int BranchNum;
     535             :     int LastNodeOnBranch;
     536             : 
     537     7455662 :     if (!state.dataPlnt->PlantLoop(LoopNum).LoopHasConnectionComp) {
     538    14911324 :         if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag &&
     539    14911324 :             state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Exists && !FirstHVACIteration) {
     540             :             // Find mixer outlet node number
     541     1439941 :             MixerOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.NodeNumOut;
     542             :             // Find splitter inlet node number
     543     1439941 :             SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.NodeNumIn;
     544             : 
     545     1439941 :             AbsDifference =
     546     1439941 :                 std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate);
     547     1439941 :             if (AbsDifference > DataBranchAirLoopPlant::MassFlowTolerance) {
     548           0 :                 if (state.dataPlnt->PlantLoop(LoopNum).MFErrIndex1 == 0) {
     549           0 :                     ShowSevereMessage(state, "Plant flows do not resolve -- splitter inlet flow does not match mixer outlet flow ");
     550           0 :                     ShowContinueErrorTimeStamp(state, "");
     551           0 :                     ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
     552           0 :                     ShowContinueError(state,
     553           0 :                                       format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
     554           0 :                     ShowContinueError(
     555           0 :                         state, format("Mixer outlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate));
     556           0 :                     ShowContinueError(
     557           0 :                         state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
     558           0 :                     ShowContinueError(
     559           0 :                         state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
     560           0 :                     ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
     561             :                 }
     562           0 :                 ShowRecurringSevereErrorAtEnd(state,
     563           0 :                                               "Plant Flows (Loop=" + state.dataPlnt->PlantLoop(LoopNum).Name +
     564             :                                                   ") splitter inlet flow not match mixer outlet flow",
     565           0 :                                               state.dataPlnt->PlantLoop(LoopNum).MFErrIndex1,
     566             :                                               AbsDifference,
     567             :                                               AbsDifference,
     568             :                                               _,
     569             :                                               "kg/s",
     570             :                                               "kg/s");
     571           0 :                 if (AbsDifference > DataBranchAirLoopPlant::MassFlowTolerance * 10.0) {
     572           0 :                     ShowSevereError(state, "Plant flows do not resolve -- splitter inlet flow does not match mixer outlet flow ");
     573           0 :                     ShowContinueErrorTimeStamp(state, "");
     574           0 :                     ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
     575           0 :                     ShowContinueError(state,
     576           0 :                                       format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
     577           0 :                     ShowContinueError(
     578           0 :                         state, format("Mixer outlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate));
     579           0 :                     ShowContinueError(
     580           0 :                         state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
     581           0 :                     ShowContinueError(
     582           0 :                         state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
     583           0 :                     ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
     584           0 :                     ShowFatalError(state, "CheckPlantMixerSplitterConsistency: Simulation terminated because of problems in plant flow resolver");
     585             :                 }
     586             :             }
     587             : 
     588             :             // now check inside s/m to see if there are problems
     589             : 
     590             :             // loop across branch outlet nodes and check mass continuity
     591     1439941 :             NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes;
     592     1439941 :             SumOutletFlow = 0.0;
     593             :             //  SumInletFlow = 0.0;
     594     7140758 :             for (OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
     595     5700817 :                 BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.BranchNumOut(OutletNum);
     596     5700817 :                 LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumOut;
     597     5700817 :                 SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
     598             :                 //  FirstNodeOnBranch= PlantLoop(LoopNum)%LoopSide(LoopSideNum)%Branch(BranchNum)%NodeNumIn
     599             :                 //  SumInletFlow = SumInletFlow + Node(FirstNodeOnBranch)%MassFlowRate
     600             :             }
     601     1439941 :             AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
     602     1439941 :             if (AbsDifference > CriteriaDelta_MassFlowRate) {
     603           0 :                 if (state.dataPlnt->PlantLoop(LoopNum).MFErrIndex2 == 0) {
     604           0 :                     ShowSevereMessage(state, "Plant flows do not resolve -- splitter inlet flow does not match branch outlet flows");
     605           0 :                     ShowContinueErrorTimeStamp(state, "");
     606           0 :                     ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
     607           0 :                     ShowContinueError(state,
     608           0 :                                       format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
     609           0 :                     ShowContinueError(state, format("Sum of Branch outlet mass flow rates= {:.6R} {{kg/s}}", SumOutletFlow));
     610           0 :                     ShowContinueError(
     611           0 :                         state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
     612           0 :                     ShowContinueError(
     613           0 :                         state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
     614           0 :                     ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
     615             :                 }
     616           0 :                 ShowRecurringSevereErrorAtEnd(state,
     617           0 :                                               "Plant Flows (Loop=" + state.dataPlnt->PlantLoop(LoopNum).Name +
     618             :                                                   ") splitter inlet flow does not match branch outlet flows",
     619           0 :                                               state.dataPlnt->PlantLoop(LoopNum).MFErrIndex2,
     620             :                                               AbsDifference,
     621             :                                               AbsDifference,
     622             :                                               _,
     623             :                                               "kg/s",
     624             :                                               "kg/s");
     625             :             }
     626             :         }
     627             :     }
     628     7455662 : }
     629             : 
     630     7455662 : void CheckForRunawayPlantTemps(EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation LoopSideNum)
     631             : {
     632             : 
     633             :     // SUBROUTINE INFORMATION:
     634             :     //       AUTHOR         Brent Griffith
     635             :     //       DATE WRITTEN   Sept 2010
     636             :     //       MODIFIED       na
     637             :     //       RE-ENGINEERED  na
     638             : 
     639             :     // PURPOSE OF THIS SUBROUTINE:
     640             :     // Check for plant control errors revealed as run away fluid temps
     641             :     //  halt program so it won't siliently run in out of control state
     642             : 
     643             :     // METHODOLOGY EMPLOYED:
     644             :     //  compare plant temps to plant min and max and halt if things run away
     645             :     //  sensitivity can be adjusted with parameters, picked somewhat arbitrary
     646             : 
     647             :     // REFERENCES:
     648             :     // na
     649             : 
     650             :     // Using/Aliasing
     651             : 
     652             :     // Locals
     653             :     // SUBROUTINE ARGUMENT DEFINITIONS:
     654             : 
     655             :     // SUBROUTINE PARAMETER DEFINITIONS:
     656     7455662 :     Real64 constexpr OverShootOffset(5.0);
     657     7455662 :     Real64 constexpr UnderShootOffset(5.0);
     658     7455662 :     Real64 constexpr FatalOverShootOffset(200.0);
     659     7455662 :     Real64 constexpr FatalUnderShootOffset(100.0);
     660             :     // INTERFACE BLOCK SPECIFICATIONS:
     661             :     // na
     662             : 
     663             :     // DERIVED TYPE DEFINITIONS:
     664             :     // na
     665             : 
     666             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     667     7455662 :     std::string hotcold;
     668             :     bool makefatalerror;
     669             :     int BrN;
     670             :     int CpN;
     671             :     Real64 LoopCapacity;
     672             :     Real64 LoopDemandSideCapacity;
     673             :     Real64 LoopSupplySideCapacity;
     674             :     Real64 DispatchedCapacity;
     675             :     Real64 LoopDemandSideDispatchedCapacity;
     676             :     Real64 LoopSupplySideDispatchedCapacity;
     677             : 
     678     7455662 :     makefatalerror = false;
     679     7455662 :     if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
     680     7455662 :         (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + OverShootOffset)) {
     681             : 
     682             :         // first stage, throw recurring warning that plant loop is getting out of control
     683       27888 :         ShowRecurringWarningErrorAtEnd(state,
     684       18592 :                                        "Plant loop exceeding upper temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name + "\"",
     685        9296 :                                        state.dataPlnt->PlantLoop(LoopNum).MaxTempErrIndex,
     686        9296 :                                        state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
     687             : 
     688        9296 :         if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
     689        9296 :             (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + FatalOverShootOffset)) {
     690           0 :             hotcold = "hot";
     691           0 :             makefatalerror = true;
     692             :         }
     693             :     }
     694             : 
     695     7455662 :     if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
     696     7455662 :         (state.dataPlnt->PlantLoop(LoopNum).MinTemp - UnderShootOffset)) {
     697             : 
     698             :         // first stage, throw recurring warning that plant loop is getting out of control
     699        4980 :         ShowRecurringWarningErrorAtEnd(state,
     700        2490 :                                        "Plant loop falling below lower temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name +
     701             :                                            "\"",
     702        1245 :                                        state.dataPlnt->PlantLoop(LoopNum).MinTempErrIndex,
     703             :                                        _,
     704        1245 :                                        state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
     705             : 
     706        1245 :         if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
     707        1245 :             (state.dataPlnt->PlantLoop(LoopNum).MinTemp - FatalUnderShootOffset)) {
     708           0 :             hotcold = "cold";
     709           0 :             makefatalerror = true;
     710             :         }
     711             :     }
     712             : 
     713     7455662 :     if (makefatalerror) {
     714           0 :         ShowSevereError(state, format("Plant temperatures are getting far too {}, check controls and relative loads and capacities", hotcold));
     715           0 :         ShowContinueErrorTimeStamp(state, "");
     716           0 :         ShowContinueError(state,
     717           0 :                           format("PlantLoop Name ({} Side) = {}",
     718           0 :                                  DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
     719           0 :                                  state.dataPlnt->PlantLoop(LoopNum).Name));
     720           0 :         ShowContinueError(state,
     721           0 :                           format("PlantLoop Setpoint Temperature={:.1R} {{C}}",
     722           0 :                                  state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).TempSetPointNodeNum).TempSetPoint));
     723           0 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt) {
     724           0 :             ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Supply) has a Setpoint.");
     725             :         } else {
     726           0 :             ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Supply) does not have a Setpoint.");
     727             :         }
     728           0 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
     729           0 :             ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Demand) has a Setpoint.");
     730             :         } else {
     731           0 :             ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Demand) does not have a Setpoint.");
     732             :         }
     733           0 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).OutletNodeSetPt) {
     734           0 :             ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Supply) has a Setpoint.");
     735             :         } else {
     736           0 :             ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Supply) does not have a Setpoint.");
     737             :         }
     738           0 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).OutletNodeSetPt) {
     739           0 :             ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Demand) has a Setpoint.");
     740             :         } else {
     741           0 :             ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Demand) does not have a Setpoint.");
     742             :         }
     743           0 :         ShowContinueError(state,
     744           0 :                           format("PlantLoop Outlet Node ({}Side) \"{}\" has temperature={:.1R} {{C}}",
     745           0 :                                  DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
     746           0 :                                  state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut),
     747           0 :                                  state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp));
     748           0 :         ShowContinueError(state,
     749           0 :                           format("PlantLoop Inlet Node ({}Side) \"{}\" has temperature={:.1R} {{C}}",
     750           0 :                                  DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
     751           0 :                                  state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumIn),
     752           0 :                                  state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumIn).Temp));
     753           0 :         ShowContinueError(state, format("PlantLoop Minimum Temperature={:.1R} {{C}}", state.dataPlnt->PlantLoop(LoopNum).MinTemp));
     754           0 :         ShowContinueError(state, format("PlantLoop Maximum Temperature={:.1R} {{C}}", state.dataPlnt->PlantLoop(LoopNum).MaxTemp));
     755           0 :         ShowContinueError(state,
     756           0 :                           format("PlantLoop Flow Request (LoopSideLocation::Supply)={:.1R} {{kg/s}}",
     757           0 :                                  state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).FlowRequest));
     758           0 :         ShowContinueError(state,
     759           0 :                           format("PlantLoop Flow Request (LoopSideLocation::Demand)={:.1R} {{kg/s}}",
     760           0 :                                  state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).FlowRequest));
     761           0 :         ShowContinueError(state,
     762           0 :                           format("PlantLoop Node ({}Side) \"{}\" has mass flow rate ={:.1R} {{kg/s}}",
     763           0 :                                  DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
     764           0 :                                  state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut),
     765           0 :                                  state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).MassFlowRate));
     766           0 :         ShowContinueError(state,
     767           0 :                           format("PlantLoop PumpHeat (LoopSideLocation::Supply)={:.1R} {{W}}",
     768           0 :                                  state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).TotalPumpHeat));
     769           0 :         ShowContinueError(state,
     770           0 :                           format("PlantLoop PumpHeat (LoopSideLocation::Demand)={:.1R} {{W}}",
     771           0 :                                  state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).TotalPumpHeat));
     772           0 :         ShowContinueError(state, format("PlantLoop Cooling Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).CoolingDemand));
     773           0 :         ShowContinueError(state, format("PlantLoop Heating Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).HeatingDemand));
     774           0 :         ShowContinueError(state, format("PlantLoop Demand not Dispatched={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).DemandNotDispatched));
     775           0 :         ShowContinueError(state, format("PlantLoop Unmet Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).UnmetDemand));
     776             : 
     777           0 :         LoopCapacity = 0.0;
     778           0 :         DispatchedCapacity = 0.0;
     779           0 :         for (DataPlant::LoopSideLocation LSN : DataPlant::LoopSideKeys) {
     780           0 :             for (BrN = 1; BrN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).TotalBranches; ++BrN) {
     781           0 :                 for (CpN = 1; CpN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).TotalComponents; ++CpN) {
     782           0 :                     LoopCapacity += state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).MaxLoad;
     783           0 :                     DispatchedCapacity += std::abs(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).MyLoad);
     784             :                 }
     785             :             }
     786           0 :             if (LSN == DataPlant::LoopSideLocation::Demand) {
     787           0 :                 LoopDemandSideCapacity = LoopCapacity;
     788           0 :                 LoopDemandSideDispatchedCapacity = DispatchedCapacity;
     789             :             } else {
     790           0 :                 LoopSupplySideCapacity = LoopCapacity - LoopDemandSideCapacity;
     791           0 :                 LoopSupplySideDispatchedCapacity = DispatchedCapacity - LoopDemandSideDispatchedCapacity;
     792             :             }
     793             :         }
     794           0 :         ShowContinueError(state, format("PlantLoop Capacity={:.1R} {{W}}", LoopCapacity));
     795           0 :         ShowContinueError(state, format("PlantLoop Capacity (LoopSideLocation::Supply)={:.1R} {{W}}", LoopSupplySideCapacity));
     796           0 :         ShowContinueError(state, format("PlantLoop Capacity (LoopSideLocation::Demand)={:.1R} {{W}}", LoopDemandSideCapacity));
     797           0 :         ShowContinueError(state, format("PlantLoop Operation Scheme={}", state.dataPlnt->PlantLoop(LoopNum).OperationScheme));
     798           0 :         ShowContinueError(state, format("PlantLoop Operation Dispatched Load = {:.1R} {{W}}", DispatchedCapacity));
     799           0 :         ShowContinueError(state,
     800           0 :                           format("PlantLoop Operation Dispatched Load (LoopSideLocation::Supply)= {:.1R} {{W}}", LoopSupplySideDispatchedCapacity));
     801           0 :         ShowContinueError(state,
     802           0 :                           format("PlantLoop Operation Dispatched Load (LoopSideLocation::Demand)= {:.1R} {{W}}", LoopDemandSideDispatchedCapacity));
     803           0 :         ShowContinueError(state, "Branches on the Loop.");
     804           0 :         ShowBranchesOnLoop(state, LoopNum);
     805           0 :         ShowContinueError(state, "*************************");
     806           0 :         ShowContinueError(state, "Possible things to look for to correct this problem are:");
     807           0 :         ShowContinueError(state, "  Capacity, Operation Scheme, Mass flow problems, Pump Heat building up over time.");
     808           0 :         ShowContinueError(state, "  Try a shorter runperiod to stop before it fatals and look at");
     809           0 :         ShowContinueError(state, "    lots of node time series data to see what is going wrong.");
     810           0 :         ShowContinueError(state, "  If this is happening during Warmup, you can use Output:Diagnostics,ReportDuringWarmup;");
     811           0 :         ShowContinueError(state, "  This is detected at the loop level, but the typical problems are in the components.");
     812           0 :         ShowFatalError(state, format("CheckForRunawayPlantTemps: Simulation terminated because of run away plant temperatures, too {}", hotcold));
     813             :     }
     814     7455662 : }
     815             : 
     816     5981702 : void SetAllFlowLocks(EnergyPlusData &state, DataPlant::FlowLock const Value)
     817             : {
     818             : 
     819             :     // SUBROUTINE INFORMATION:
     820             :     //       AUTHOR         Edwin Lee
     821             :     //       DATE WRITTEN   November 2009
     822             :     //       MODIFIED       na
     823             :     //       RE-ENGINEERED  na
     824             : 
     825             :     // PURPOSE OF THIS SUBROUTINE:
     826             :     // This subroutine will set both LoopSide flowlocks on all plant loops to the input value (0 or 1)
     827             :     // Initially this routine is used as a quick replacement for the FlowLock=0 and FlowLock=1 statements
     828             :     //  in order to provide the same behavior through phase I of the demand side rewrite
     829             :     // Eventually this routine may be employed again to quickly initialize all loops once phase III is complete
     830    13835437 :     for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
     831    23561205 :         for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
     832    15707470 :             state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).FlowLock = Value;
     833             :         }
     834             :     }
     835     5981702 : }
     836             : 
     837     5981244 : void ResetAllPlantInterConnectFlags(EnergyPlusData &state)
     838             : {
     839             : 
     840             :     // SUBROUTINE INFORMATION:
     841             :     //       AUTHOR         Edwin Lee
     842             :     //       DATE WRITTEN   September 2010
     843             :     //       MODIFIED       na
     844             :     //       RE-ENGINEERED  na
     845             : 
     846             :     // PURPOSE OF THIS SUBROUTINE:
     847             :     // This subroutine will reset all interconnected (air, zone, etc.) sim flags for both loopsides of all loops
     848             : 
     849    13833832 :     for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
     850    23557764 :         for (auto &e : state.dataPlnt->PlantLoop(LoopNum).LoopSide) {
     851    15705176 :             e.SimAirLoopsNeeded = false;
     852    15705176 :             e.SimZoneEquipNeeded = false;
     853    15705176 :             e.SimNonZoneEquipNeeded = false;
     854    15705176 :             e.SimElectLoadCentrNeeded = false;
     855             :         }
     856             :     }
     857     5981244 : }
     858             : 
     859    18323094 : void PullCompInterconnectTrigger(EnergyPlusData &state,
     860             :                                  const PlantLocation &plantLoc,              // Component Location
     861             :                                  int &UniqueCriteriaCheckIndex,              // An integer given to this particular check
     862             :                                  const PlantLocation &ConnectedPlantLoc,     // Interconnected Component's Location
     863             :                                  const DataPlant::CriteriaType CriteriaType, // The criteria check to use, see DataPlant: SimFlagCriteriaTypes
     864             :                                  const Real64 CriteriaValue                  // The value of the criteria check to evaluate
     865             : )
     866             : {
     867             : 
     868             :     // SUBROUTINE INFORMATION:
     869             :     //       AUTHOR         Edwin Lee
     870             :     //       DATE WRITTEN   September 2010
     871             :     //       MODIFIED       na
     872             :     //       RE-ENGINEERED  na
     873             : 
     874             :     // PURPOSE OF THIS SUBROUTINE:
     875             :     // Provides a generic means for components to trigger interconnected loop sides sim flags
     876             : 
     877             :     // METHODOLOGY EMPLOYED:
     878             :     // Determine convergence criteria based on *CriteriaType* variable.  This routine only turns
     879             :     //  the loop side sim flag ON, it doesn't turn it OFF.
     880             :     // The convergence value history was originally going to be put at the Branch()%Comp()%...
     881             :     //  level, but this would be quite difficult if we had multiple convergence checks for the
     882             :     //  same component, such as if a chiller was trying to turn on the condenser side and the
     883             :     //  heat recovery side.
     884             :     // It was determined to use a local array, which is only reallocated during the first stages
     885             :     //  of the simulation when components are first calling their sim flag requests.  After that
     886             :     //  the INOUT index variable will be used to avoid reallocation and string compares.
     887             :     // Error handling will be put in to ensure unique identifiers are used for debugging purposes.
     888             :     // A single component may have multiple check indeces, but a single index will only have one
     889             :     //  associated component.  Therefore whenever we come in with a non-zero index, we will just
     890             :     //  verify that the stored loop/side/branch/comp matches
     891             : 
     892             :     // Using/Aliasing
     893             :     using DataPlant::CriteriaDelta_HeatTransferRate;
     894             :     using DataPlant::CriteriaDelta_MassFlowRate;
     895             :     using DataPlant::CriteriaDelta_Temperature;
     896             : 
     897    18323094 :     CriteriaData CurCriteria; // for convenience
     898             : 
     899    18323094 :     if (UniqueCriteriaCheckIndex <= 0) { // If we don't yet have an index, we need to initialize
     900             : 
     901             :         // We need to start by allocating, or REallocating the array
     902         330 :         int const CurrentNumChecksStored(static_cast<int>(state.dataPlantUtilities->CriteriaChecks.size() + 1));
     903         330 :         state.dataPlantUtilities->CriteriaChecks.redimension(CurrentNumChecksStored);
     904             : 
     905             :         // Store the unique name and location
     906         330 :         state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopNum = plantLoc.loopNum;
     907         330 :         state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopSideNum = plantLoc.loopSideNum;
     908         330 :         state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompBranchNum = plantLoc.branchNum;
     909         330 :         state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompCompNum = plantLoc.compNum;
     910             : 
     911             :         // Since this was the first pass, it is safe to assume something has changed!
     912             :         // Therefore we'll set the sim flag to true
     913         330 :         state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
     914             : 
     915             :         // Make sure we return the proper value of index
     916         330 :         UniqueCriteriaCheckIndex = CurrentNumChecksStored;
     917             : 
     918             :     } else { // We already have an index
     919             : 
     920             :         // If we have an index, we need to do a brief error handling, then determine
     921             :         //  sim flag status based on the criteria type
     922             : 
     923             :         // First store the current check in a single variable instead of array for readability
     924    18322764 :         CurCriteria = state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex);
     925             : 
     926             :         // Check to make sure we didn't reuse the index in multiple components
     927    18322764 :         if (CurCriteria.CallingCompLoopNum != plantLoc.loopNum || CurCriteria.CallingCompLoopSideNum != plantLoc.loopSideNum ||
     928    18322764 :             CurCriteria.CallingCompBranchNum != plantLoc.branchNum || CurCriteria.CallingCompCompNum != plantLoc.compNum) {
     929             :             // Diagnostic fatal: component does not properly utilize unique indexing
     930             :         }
     931             : 
     932             :         // Initialize, then check if we are out of range
     933    18322764 :         switch (CriteriaType) {
     934    18322764 :         case DataPlant::CriteriaType::MassFlowRate: {
     935    18322764 :             if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > CriteriaDelta_MassFlowRate) {
     936      175685 :                 state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
     937             :             }
     938    18322764 :         } break;
     939           0 :         case DataPlant::CriteriaType::Temperature: {
     940           0 :             if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > CriteriaDelta_Temperature) {
     941           0 :                 state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
     942             :             }
     943           0 :         } break;
     944           0 :         case DataPlant::CriteriaType::HeatTransferRate: {
     945           0 :             if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > CriteriaDelta_HeatTransferRate) {
     946           0 :                 state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
     947             :             }
     948           0 :         } break;
     949           0 :         default:
     950             :             // Diagnostic fatal: improper criteria type
     951           0 :             break;
     952             :         }
     953             : 
     954             :     } // if we have an index or not
     955             : 
     956             :     // Store the value for the next pass
     957    18323094 :     state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex).ThisCriteriaCheckValue = CriteriaValue;
     958    18323094 : }
     959             : 
     960    11903318 : void UpdateChillerComponentCondenserSide(EnergyPlusData &state,
     961             :                                          int const LoopNum,                                   // component's loop index
     962             :                                          const DataPlant::LoopSideLocation LoopSide,          // component's loop side number
     963             :                                          [[maybe_unused]] DataPlant::PlantEquipmentType Type, // Component's type index
     964             :                                          int const InletNodeNum,                              // Component's inlet node pointer
     965             :                                          int const OutletNodeNum,                             // Component's outlet node pointer
     966             :                                          Real64 const ModelCondenserHeatRate,                 // model's heat rejection rate at condenser (W)
     967             :                                          Real64 const ModelInletTemp,                         // model's inlet temperature (C)
     968             :                                          Real64 const ModelOutletTemp,                        // model's outlet temperature (C)
     969             :                                          Real64 const ModelMassFlowRate,                      // model's condenser water mass flow rate (kg/s)
     970             :                                          bool const FirstHVACIteration)
     971             : {
     972             : 
     973             :     // SUBROUTINE INFORMATION:
     974             :     //       AUTHOR         Brent Griffith
     975             :     //       DATE WRITTEN   February 2010
     976             :     //       MODIFIED       na
     977             :     //       RE-ENGINEERED  na
     978             : 
     979             :     // PURPOSE OF THIS SUBROUTINE:
     980             :     // provides reusable update routine for water cooled chiller's condenser water
     981             :     // connection to plant loops
     982             : 
     983             :     // METHODOLOGY EMPLOYED:
     984             :     // check if anything changed or doesn't agree and set simulation flags.
     985             :     // update outlet conditions if needed or possible
     986             : 
     987             :     // Using/Aliasing
     988             :     using FluidProperties::GetSpecificHeatGlycol;
     989             : 
     990             :     // SUBROUTINE PARAMETER DEFINITIONS:
     991             :     static constexpr std::string_view RoutineName("UpdateChillerComponentCondenserSide");
     992             : 
     993             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     994    11903318 :     bool DidAnythingChange(false);             // set to true if conditions changed
     995             :     int OtherLoopNum;                          // local loop pointer for remote connected loop
     996             :     DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
     997             :     int ConnectLoopNum;                        // local do loop counter
     998             :     Real64 Cp;
     999             : 
    1000             :     // check if any conditions have changed
    1001    11903318 :     if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
    1002             : 
    1003    11903318 :     if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
    1004             : 
    1005    11903318 :     if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) DidAnythingChange = true;
    1006             : 
    1007    11903318 :     if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) DidAnythingChange = true;
    1008             : 
    1009             :     // could also check heat rate against McDeltaT from node data
    1010             : 
    1011    11903318 :     if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelCondenserHeatRate > 0.0)) {
    1012             : 
    1013             :         // TODO also send a request that condenser loop be made available, interlock message infrastructure??
    1014             : 
    1015        6598 :         DidAnythingChange = true;
    1016             :     }
    1017             : 
    1018    11903318 :     if (DidAnythingChange || FirstHVACIteration) {
    1019             :         // use current mass flow rate and inlet temp from Node and recalculate outlet temp
    1020     8056085 :         if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
    1021             :             // update node outlet conditions
    1022     3874334 :             Cp = GetSpecificHeatGlycol(
    1023     3874334 :                 state, state.dataPlnt->PlantLoop(LoopNum).FluidName, ModelInletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
    1024     3874334 :             state.dataLoopNodes->Node(OutletNodeNum).Temp =
    1025     3874334 :                 state.dataLoopNodes->Node(InletNodeNum).Temp + ModelCondenserHeatRate / (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate * Cp);
    1026             :         }
    1027             :         // see 10133
    1028             :         // state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate =
    1029             :         //    state.dataLoopNodes->Node(InletNodeNum)
    1030             :         //        .MassFlowRate; // if condenser is on inlet branch, the outlet node needs to be updated or can get splitter/mixer failures
    1031             : 
    1032             :         // set sim flag for this loop
    1033     8056085 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
    1034             : 
    1035             :         // set sim flag on connected loops to true because this side changed
    1036     8056085 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
    1037    22156859 :             for (ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
    1038             :                 // see 10133
    1039    14108871 :                 if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
    1040             :                     // full chiller model is not really run when called from the condenser side and the chiller evap loop puts demand on the condenser
    1041             :                     // loop, so this logic is flawed, remove if statement so evap side gets simulated again
    1042           0 :                     OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
    1043           0 :                     OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
    1044           0 :                     state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
    1045             :                 }
    1046             :             }
    1047             :         }
    1048             : 
    1049             :     } else { // nothing changed so turn off sim flag
    1050     3847233 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
    1051             :     }
    1052    11903318 : }
    1053             : 
    1054      489304 : void UpdateComponentHeatRecoverySide(EnergyPlusData &state,
    1055             :                                      int const LoopNum,                                   // component's loop index
    1056             :                                      const DataPlant::LoopSideLocation LoopSide,          // component's loop side number
    1057             :                                      [[maybe_unused]] DataPlant::PlantEquipmentType Type, // Component's type index
    1058             :                                      int const InletNodeNum,                              // Component's inlet node pointer
    1059             :                                      int const OutletNodeNum,                             // Component's outlet node pointer
    1060             :                                      Real64 const ModelRecoveryHeatRate,                  // model's heat rejection rate at recovery (W)
    1061             :                                      Real64 const ModelInletTemp,                         // model's inlet temperature (C)
    1062             :                                      Real64 const ModelOutletTemp,                        // model's outlet temperature (C)
    1063             :                                      Real64 const ModelMassFlowRate,                      // model's condenser water mass flow rate (kg/s)
    1064             :                                      bool const FirstHVACIteration)
    1065             : {
    1066             : 
    1067             :     // SUBROUTINE INFORMATION:
    1068             :     //       AUTHOR         Brent Griffith
    1069             :     //       DATE WRITTEN   Sept 2010
    1070             :     //       MODIFIED       na
    1071             :     //       RE-ENGINEERED  na
    1072             : 
    1073             :     // PURPOSE OF THIS SUBROUTINE:
    1074             :     // provides reusable update routine for heat recovery type
    1075             :     // connection to plant loops
    1076             : 
    1077             :     // METHODOLOGY EMPLOYED:
    1078             :     // check if anything changed or doesn't agree and set simulation flags.
    1079             :     // update outlet conditions if needed or possible
    1080             : 
    1081             :     // Using/Aliasing
    1082             :     using FluidProperties::GetSpecificHeatGlycol;
    1083             : 
    1084             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1085             :     static constexpr std::string_view RoutineName("UpdateComponentHeatRecoverySide");
    1086             : 
    1087             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1088      489304 :     bool DidAnythingChange(false);             // set to true if conditions changed
    1089             :     int OtherLoopNum;                          // local loop pointer for remote connected loop
    1090             :     DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
    1091             :     int ConnectLoopNum;                        // local do loop counter
    1092             :     Real64 Cp;                                 // local fluid specific heat
    1093             : 
    1094             :     // check if any conditions have changed
    1095      489304 :     if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
    1096             : 
    1097      489304 :     if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
    1098             : 
    1099      489304 :     if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) DidAnythingChange = true;
    1100             : 
    1101      489304 :     if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) DidAnythingChange = true;
    1102             : 
    1103             :     // could also check heat rate against McDeltaT from node data
    1104             : 
    1105      489304 :     if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelRecoveryHeatRate > 0.0)) {
    1106             :         // no flow but trying to move heat to this loop problem!
    1107             : 
    1108          10 :         DidAnythingChange = true;
    1109             :     }
    1110             : 
    1111      489304 :     if (DidAnythingChange || FirstHVACIteration) {
    1112             :         // use current mass flow rate and inlet temp from Node and recalculate outlet temp
    1113      387029 :         if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
    1114             :             // update node outlet conditions
    1115      235527 :             Cp = GetSpecificHeatGlycol(
    1116      235527 :                 state, state.dataPlnt->PlantLoop(LoopNum).FluidName, ModelInletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
    1117      235527 :             state.dataLoopNodes->Node(OutletNodeNum).Temp =
    1118      235527 :                 state.dataLoopNodes->Node(InletNodeNum).Temp + ModelRecoveryHeatRate / (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate * Cp);
    1119             :         }
    1120             :         // see 10133
    1121             :         // state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate =
    1122             :         //    state.dataLoopNodes->Node(InletNodeNum).MassFlowRate; // if heat recovery bundle is on inlet branch, the mass flow needs to be
    1123             :         //// updated or get splitter/mixer failures
    1124             : 
    1125             :         // set sim flag for this loop
    1126      387029 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
    1127             : 
    1128             :         // set sim flag on connected loops to true because this side changed
    1129      387029 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
    1130      799617 :             for (ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
    1131             :                 // see 10133
    1132      533078 :                 if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
    1133             :                     // full chiller model is not really run when called from the heat recovery side and the chiller evap loop puts demand on the heat
    1134             :                     // recovery loop, so this logic is flawed, remove if statement so evap side gets simulated again
    1135      266539 :                     OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
    1136      266539 :                     OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
    1137      266539 :                     state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
    1138             :                 }
    1139             :             }
    1140             :         }
    1141             : 
    1142             :     } else { // nothing changed so turn off sim flag
    1143      102275 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
    1144             :     }
    1145      489304 : }
    1146             : 
    1147       22253 : void UpdateAbsorberChillerComponentGeneratorSide(EnergyPlusData &state,
    1148             :                                                  int const LoopNum,                                                 // component's loop index
    1149             :                                                  const DataPlant::LoopSideLocation LoopSide,                        // component's loop side number
    1150             :                                                  [[maybe_unused]] DataPlant::PlantEquipmentType const Type,         // Component's type index
    1151             :                                                  int const InletNodeNum,                                            // Component's inlet node pointer
    1152             :                                                  [[maybe_unused]] int const OutletNodeNum,                          // Component's outlet node pointer
    1153             :                                                  [[maybe_unused]] DataLoopNode::NodeFluidType const HeatSourceType, // Type of fluid in Generator loop
    1154             :                                                  Real64 const ModelGeneratorHeatRate,                               // model's generator heat rate (W)
    1155             :                                                  Real64 const ModelMassFlowRate, // model's generator mass flow rate (kg/s)
    1156             :                                                  bool const FirstHVACIteration)
    1157             : {
    1158             : 
    1159             :     // SUBROUTINE INFORMATION:
    1160             :     //       AUTHOR         Brent Griffith
    1161             :     //       DATE WRITTEN   February 2010
    1162             :     //       MODIFIED       na
    1163             :     //       RE-ENGINEERED  na
    1164             : 
    1165             :     // PURPOSE OF THIS SUBROUTINE:
    1166             :     // provides reusable update routine for absoption chiller's generator
    1167             :     // connection to plant loops
    1168             : 
    1169             :     // METHODOLOGY EMPLOYED:
    1170             :     // check if anything changed or doesn't agree and set simulation flags.
    1171             :     // update outlet conditions if needed or possible
    1172             : 
    1173             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1174       22253 :     bool DidAnythingChange(false);             // set to true if conditions changed
    1175             :     int OtherLoopNum;                          // local loop pointer for remote connected loop
    1176             :     DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
    1177             :     int ConnectLoopNum;                        // local do loop counter
    1178             : 
    1179             :     // check if any conditions have changed
    1180       22253 :     if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
    1181             : 
    1182       22253 :     if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelGeneratorHeatRate > 0.0)) {
    1183             : 
    1184             :         //  TODO also send a request that generator loop be made available, interlock message infrastructure??
    1185             : 
    1186           0 :         DidAnythingChange = true;
    1187             :     }
    1188             : 
    1189       22253 :     if (DidAnythingChange || FirstHVACIteration) {
    1190             : 
    1191             :         // set sim flag for this loop
    1192       13521 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
    1193             : 
    1194             :         // set sim flag on connected loops to true because this side changed
    1195       13521 :         if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
    1196       40563 :             for (ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
    1197       27042 :                 if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
    1198       13521 :                     OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
    1199       13521 :                     OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
    1200       13521 :                     state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
    1201             :                 }
    1202             :             }
    1203             :         }
    1204             : 
    1205             :     } else { // nothing changed so turn off sim flag
    1206        8732 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
    1207             :     }
    1208       22253 : }
    1209             : 
    1210         456 : void InterConnectTwoPlantLoopSides(EnergyPlusData &state,
    1211             :                                    PlantLocation const &Loop1PlantLoc,
    1212             :                                    PlantLocation const &Loop2PlantLoc,
    1213             :                                    DataPlant::PlantEquipmentType ComponentType,
    1214             :                                    bool const Loop1DemandsOnLoop2)
    1215             : {
    1216             : 
    1217             :     // SUBROUTINE INFORMATION:
    1218             :     //       AUTHOR         B. Griffith
    1219             :     //       DATE WRITTEN   February 2010
    1220             :     //       MODIFIED       na
    1221             :     //       RE-ENGINEERED  na
    1222             : 
    1223             :     // PURPOSE OF THIS SUBROUTINE:
    1224             :     // Setup PlantLoop data structure pointers to direct interacting loops
    1225             : 
    1226             :     // Using/Aliasing
    1227             :     using DataPlant::ConnectedLoopData;
    1228             : 
    1229         456 :     if (Loop1PlantLoc.loopNum == 0 || Loop1PlantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid || Loop2PlantLoc.loopNum == 0 ||
    1230         456 :         Loop2PlantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
    1231           0 :         return; // Associated ScanPlantLoopsForObject couldn't find the component in the the plant loop structure...
    1232             :     }           // This is a Fatal error condition
    1233             : 
    1234         456 :     bool const Loop2DemandsOnLoop1(!Loop1DemandsOnLoop2);
    1235             : 
    1236             :     int TotalConnected;
    1237             : 
    1238         456 :     auto &loop_side_1 = state.dataPlnt->PlantLoop(Loop1PlantLoc.loopNum).LoopSide(Loop1PlantLoc.loopSideNum);
    1239         456 :     auto &connected_1 = loop_side_1.Connected;
    1240         456 :     if (allocated(connected_1)) {
    1241         150 :         TotalConnected = ++loop_side_1.TotalConnected;
    1242         150 :         connected_1.redimension(TotalConnected);
    1243             :     } else {
    1244         306 :         TotalConnected = loop_side_1.TotalConnected = 1;
    1245         306 :         connected_1.allocate(1);
    1246             :     }
    1247         456 :     connected_1(TotalConnected).LoopNum = Loop2PlantLoc.loopNum;
    1248         456 :     connected_1(TotalConnected).LoopSideNum = Loop2PlantLoc.loopSideNum;
    1249         456 :     connected_1(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
    1250         456 :     connected_1(TotalConnected).LoopDemandsOnRemote = Loop1DemandsOnLoop2;
    1251             : 
    1252         456 :     auto &loop_side_2 = state.dataPlnt->PlantLoop(Loop2PlantLoc.loopNum).LoopSide(Loop2PlantLoc.loopSideNum);
    1253         456 :     auto &connected_2 = loop_side_2.Connected;
    1254         456 :     if (allocated(connected_2)) {
    1255         152 :         TotalConnected = ++loop_side_2.TotalConnected;
    1256         152 :         connected_2.redimension(TotalConnected);
    1257             :     } else {
    1258         304 :         TotalConnected = loop_side_2.TotalConnected = 1;
    1259         304 :         connected_2.allocate(1);
    1260             :     }
    1261         456 :     connected_2(TotalConnected).LoopNum = Loop1PlantLoc.loopNum;
    1262         456 :     connected_2(TotalConnected).LoopSideNum = Loop1PlantLoc.loopSideNum;
    1263         456 :     connected_2(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
    1264         456 :     connected_2(TotalConnected).LoopDemandsOnRemote = Loop2DemandsOnLoop1;
    1265             : }
    1266             : 
    1267          78 : void ShiftPlantLoopSideCallingOrder(EnergyPlusData &state, int const OldIndex, int const NewIndex)
    1268             : {
    1269             : 
    1270             :     // SUBROUTINE INFORMATION:
    1271             :     //       AUTHOR         B. Griffith
    1272             :     //       DATE WRITTEN   <April 2011
    1273             :     //       MODIFIED       na
    1274             :     //       RE-ENGINEERED  na
    1275             : 
    1276             :     // PURPOSE OF THIS SUBROUTINE:
    1277             :     // re-arrange the calling order, move one loop side from an old index to a new one
    1278             : 
    1279             :     // Using/Aliasing
    1280             :     using namespace DataPlant;
    1281             : 
    1282             :     // Object Data
    1283          78 :     PlantCallingOrderInfoStruct RecordToMoveInPlantCallingOrderInfo;
    1284             : 
    1285          78 :     if (OldIndex == 0) {
    1286           0 :         ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, Old Index=0");
    1287             :     }
    1288          78 :     if (NewIndex == 0) {
    1289           0 :         ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, New Index=1");
    1290             :     }
    1291          78 :     if ((OldIndex == 0) || (NewIndex == 0)) {
    1292           0 :         return;
    1293             :     }
    1294             : 
    1295             :     // store copy of prior structure
    1296          78 :     Array1D<PlantCallingOrderInfoStruct> TempPlantCallingOrderInfo(state.dataPlnt->PlantCallingOrderInfo);
    1297             : 
    1298          78 :     RecordToMoveInPlantCallingOrderInfo = state.dataPlnt->PlantCallingOrderInfo(OldIndex);
    1299             : 
    1300          78 :     if (OldIndex == NewIndex) {
    1301             :         // do nothing, no shift needed.
    1302          75 :     } else if ((OldIndex == 1) && (NewIndex > OldIndex) && (NewIndex < state.dataPlnt->TotNumHalfLoops)) {
    1303             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 1, NI = 5)
    1304             :         // example shifted:  2  3  4  5  1  6  7  8
    1305             : 
    1306           1 :         state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({2, NewIndex});
    1307           1 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1308           2 :         state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
    1309           3 :             TempPlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops});
    1310             : 
    1311          74 :     } else if ((OldIndex == 1) && (NewIndex > OldIndex) && (NewIndex == state.dataPlnt->TotNumHalfLoops)) {
    1312             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 1, NI = 8)
    1313             :         // example shifted:  2  3  4  5  6  7  8  1
    1314             : 
    1315           0 :         state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({2, NewIndex});
    1316           0 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1317          74 :     } else if ((OldIndex > 1) && (NewIndex > OldIndex) && (NewIndex < state.dataPlnt->TotNumHalfLoops)) {
    1318             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 3, NI = 6)
    1319             :         // example shifted:  1  2  4  5  6  3  7  8
    1320           0 :         state.dataPlnt->PlantCallingOrderInfo({1, OldIndex - 1}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
    1321           0 :         state.dataPlnt->PlantCallingOrderInfo({OldIndex, NewIndex - 1}) = TempPlantCallingOrderInfo({OldIndex + 1, NewIndex});
    1322           0 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1323           0 :         state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
    1324           0 :             TempPlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops});
    1325          74 :     } else if ((OldIndex > 1) && (NewIndex > OldIndex) && (NewIndex == state.dataPlnt->TotNumHalfLoops)) {
    1326             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 3, NI = 8)
    1327             :         // example shifted:  1  2  4  5  6  7  8  3
    1328           0 :         state.dataPlnt->PlantCallingOrderInfo({1, OldIndex - 1}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
    1329           0 :         state.dataPlnt->PlantCallingOrderInfo({OldIndex, NewIndex - 1}) = TempPlantCallingOrderInfo({OldIndex + 1, NewIndex});
    1330           0 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1331          74 :     } else if ((OldIndex > 1) && (NewIndex < OldIndex) && (NewIndex == 1)) {
    1332             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 3, NI = 1)
    1333             :         // example shifted:  3  1  2  4  5  6  7  8
    1334           6 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1335           6 :         state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
    1336          12 :         state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
    1337          18 :             TempPlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops});
    1338             : 
    1339          68 :     } else if ((OldIndex > 1) && (NewIndex < OldIndex) && (NewIndex > 1)) {
    1340             :         // example was:      1  2  3  4  5  6  7  8 (with OI = 3, NI = 2)
    1341             :         // example shifted:  1  3  2  4  5  6  7  8
    1342          68 :         state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({1, NewIndex - 1});
    1343          68 :         state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
    1344          68 :         state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({NewIndex, NewIndex + (OldIndex - NewIndex) - 1});
    1345         136 :         state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
    1346         204 :             TempPlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops});
    1347             : 
    1348             :     } else {
    1349           0 :         ShowSevereError(state,
    1350             :                         "ShiftPlantLoopSideCallingOrder: developer error notice, caught unexpected logical case in "
    1351             :                         "ShiftPlantLoopSideCallingOrder PlantUtilities");
    1352             :     }
    1353          78 : }
    1354             : 
    1355       14694 : void RegisterPlantCompDesignFlow(EnergyPlusData &state,
    1356             :                                  int const ComponentInletNodeNum, // the component's water inlet node number
    1357             :                                  Real64 const DesPlantFlow        // the component's design fluid volume flow rate [m3/s]
    1358             : )
    1359             : {
    1360             : 
    1361             :     // SUBROUTINE INFORMATION:
    1362             :     //       AUTHOR         Fred Buhl(previously SaveCompDesWaterFlow in General.cc)
    1363             :     //       DATE WRITTEN   January 2004
    1364             :     //       MODIFIED
    1365             :     //       RE-ENGINEERED  B. Griffith April 2011, allow to enter repeatedly
    1366             : 
    1367             :     // PURPOSE OF THIS SUBROUTINE:
    1368             :     // Regester the design fluid flow rates of plant components for sizing purposes
    1369             :     // in an array that can be accessed by the plant manager routines
    1370             :     // allows sizing routines to iterate by safely processing repeated calls from the same component
    1371             : 
    1372             :     // METHODOLOGY EMPLOYED:
    1373             :     // Derived from SaveCompDesWaterFlow but changed to allow re entry with the same node just update
    1374             :     // the information at the same location in the structure
    1375             :     // The design flow rate is stored in a dynamic structure array along with the plant component's inlet node number
    1376             :     // (which is used by plant as a component identifier instead if name and type).
    1377             : 
    1378             :     // Using/Aliasing
    1379             :     using namespace DataSizing;
    1380             : 
    1381             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1382             :     int NumPlantComps;
    1383             :     int PlantCompNum; // component do loop index
    1384             :     bool Found;
    1385             :     int thisCallNodeIndex;
    1386             : 
    1387       14694 :     NumPlantComps = state.dataSize->SaveNumPlantComps;
    1388             : 
    1389       14694 :     if (NumPlantComps == 0) { // first time in, fill and return
    1390         460 :         NumPlantComps = 1;
    1391         460 :         state.dataSize->CompDesWaterFlow.allocate(NumPlantComps);
    1392             :         // save the new data
    1393         460 :         state.dataSize->CompDesWaterFlow(NumPlantComps).SupNode = ComponentInletNodeNum;
    1394         460 :         state.dataSize->CompDesWaterFlow(NumPlantComps).DesVolFlowRate = DesPlantFlow;
    1395         460 :         state.dataSize->SaveNumPlantComps = NumPlantComps;
    1396         460 :         return;
    1397             :     }
    1398             : 
    1399       14234 :     Found = false;
    1400             :     // find node num index in structure if any
    1401      323577 :     for (PlantCompNum = 1; PlantCompNum <= NumPlantComps; ++PlantCompNum) {
    1402      317283 :         if (ComponentInletNodeNum == state.dataSize->CompDesWaterFlow(PlantCompNum).SupNode) {
    1403        7940 :             Found = true;
    1404        7940 :             thisCallNodeIndex = PlantCompNum;
    1405             :         }
    1406      317283 :         if (Found) break;
    1407             :     }
    1408             : 
    1409       14234 :     if (!Found) {        // grow structure and add new node at the end
    1410        6294 :         ++NumPlantComps; // increment the number of components that use water as a source of heat or coolth
    1411        6294 :         state.dataSize->CompDesWaterFlow.emplace_back(ComponentInletNodeNum, DesPlantFlow); // Append the new element
    1412        6294 :         state.dataSize->SaveNumPlantComps = NumPlantComps;
    1413             :     } else {
    1414        7940 :         state.dataSize->CompDesWaterFlow(thisCallNodeIndex).SupNode = ComponentInletNodeNum;
    1415        7940 :         state.dataSize->CompDesWaterFlow(thisCallNodeIndex).DesVolFlowRate = DesPlantFlow;
    1416             :     }
    1417             : }
    1418             : 
    1419   289969903 : void SafeCopyPlantNode(EnergyPlusData &state,
    1420             :                        int const InletNodeNum,
    1421             :                        int const OutletNodeNum,
    1422             :                        ObjexxFCL::Optional_int_const LoopNum,
    1423             :                        [[maybe_unused]] ObjexxFCL::Optional<Real64 const> OutletTemp // set on outlet node if present and water.
    1424             : )
    1425             : {
    1426             : 
    1427             :     // SUBROUTINE INFORMATION:
    1428             :     //       AUTHOR         B.  Griffith
    1429             :     //       DATE WRITTEN   February, 2010
    1430             :     //       MODIFIED       na
    1431             :     //       RE-ENGINEERED  na
    1432             : 
    1433             :     // PURPOSE OF THIS SUBROUTINE:
    1434             :     // Provide a safer alternative for Node(outlet) = Node(inlet)
    1435             :     // Intended just for plant
    1436             : 
    1437             :     // METHODOLOGY EMPLOYED:
    1438             :     // Copy over state variables but not setpoints
    1439             :     // derived from adiabatic Pipes
    1440             : 
    1441             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1442   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).FluidType = state.dataLoopNodes->Node(InletNodeNum).FluidType;
    1443             : 
    1444   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).Temp = state.dataLoopNodes->Node(InletNodeNum).Temp;
    1445   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
    1446   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).Quality = state.dataLoopNodes->Node(InletNodeNum).Quality;
    1447   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).Enthalpy =
    1448   289969903 :         state.dataLoopNodes->Node(InletNodeNum).Enthalpy; // should have routines that keep this current with temp?
    1449             : 
    1450   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).TempMin = state.dataLoopNodes->Node(InletNodeNum).TempMin;
    1451   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).TempMax = state.dataLoopNodes->Node(InletNodeNum).TempMax;
    1452   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMinAvail =
    1453   289969903 :         max(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMin, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMinAvail);
    1454   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMaxAvail =
    1455   289969903 :         min(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMax, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMaxAvail);
    1456             : 
    1457   289969903 :     state.dataLoopNodes->Node(OutletNodeNum).HumRat = state.dataLoopNodes->Node(InletNodeNum).HumRat; // air only?
    1458             : 
    1459             :     // Only pass pressure if we aren't doing a pressure simulation
    1460   289969903 :     if (present(LoopNum)) {
    1461   210943459 :         switch (state.dataPlnt->PlantLoop(LoopNum).PressureSimType) {
    1462   210868529 :         case DataPlant::PressSimType::NoPressure:
    1463   210868529 :             state.dataLoopNodes->Node(OutletNodeNum).Press = state.dataLoopNodes->Node(InletNodeNum).Press;
    1464   210943459 :         default:
    1465             :             // Don't do anything
    1466   210943459 :             break;
    1467             :         }
    1468             :     }
    1469   289969903 : }
    1470             : 
    1471   212721033 : Real64 BoundValueToNodeMinMaxAvail(EnergyPlusData &state, Real64 const ValueToBound, int const NodeNumToBoundWith)
    1472             : {
    1473             : 
    1474             :     // FUNCTION INFORMATION:
    1475             :     //       AUTHOR         Edwin Lee
    1476             :     //       DATE WRITTEN   September 2010
    1477             :     //       MODIFIED       na
    1478             :     //       RE-ENGINEERED  na
    1479             : 
    1480             :     // PURPOSE OF THIS FUNCTION:
    1481             :     // Provides a clean way to quickly bound a generic value to within any node's minavail and maxavail range
    1482             : 
    1483             :     // METHODOLOGY EMPLOYED:
    1484             :     // Bound up to min avail, down to max avail
    1485             : 
    1486             :     // Return value
    1487             :     Real64 BoundedValue;
    1488             : 
    1489   212721033 :     BoundedValue = ValueToBound;
    1490   212721033 :     BoundedValue = max(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMinAvail);
    1491   212721033 :     BoundedValue = min(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMaxAvail);
    1492             : 
    1493   212721033 :     return BoundedValue;
    1494             : }
    1495             : 
    1496    18524118 : void TightenNodeMinMaxAvails(EnergyPlusData &state, int const NodeNum, Real64 const NewMinAvail, Real64 const NewMaxAvail)
    1497             : {
    1498             : 
    1499             :     // SUBROUTINE INFORMATION:
    1500             :     //       AUTHOR         Edwin Lee
    1501             :     //       DATE WRITTEN   January, 2011
    1502             :     //       MODIFIED       na
    1503             :     //       RE-ENGINEERED  na
    1504             : 
    1505             :     // PURPOSE OF THIS SUBROUTINE:
    1506             :     // Provides a means of tightening up min/max avail on a node if possible
    1507             : 
    1508             :     // METHODOLOGY EMPLOYED:
    1509             :     // Bring up node min avail to new min avail if it doesn't violate any other node conditions
    1510             :     // Pull down node max avail to new max avail if it doesn't violate any other node conditions
    1511             :     // Assumes that current min/max avails are already honoring hardware min/max values, so they aren't checked here
    1512             : 
    1513             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1514             :     Real64 OldMinAvail;
    1515             :     Real64 OldMaxAvail;
    1516             : 
    1517    18524118 :     OldMinAvail = state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail;
    1518    18524118 :     OldMaxAvail = state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail;
    1519             : 
    1520             :     // If the new min avail is higher than previous, and it isn't higher than the max avail, update MIN AVAIL
    1521    18524118 :     if ((NewMinAvail > OldMinAvail) && (NewMinAvail <= OldMaxAvail)) state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail = NewMinAvail;
    1522             : 
    1523             :     // If the new max avail is lower than previous, and it isn't lower than the min avail, update MAX AVAIL
    1524    18524118 :     if ((NewMaxAvail < OldMaxAvail) && (NewMaxAvail >= OldMinAvail)) state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = NewMaxAvail;
    1525    18524118 : }
    1526             : 
    1527    21304399 : Real64 BoundValueToWithinTwoValues(Real64 const ValueToBound, Real64 const LowerBound, Real64 const UpperBound)
    1528             : {
    1529             : 
    1530             :     // FUNCTION INFORMATION:
    1531             :     //       AUTHOR         Edwin Lee
    1532             :     //       DATE WRITTEN   September 2010
    1533             :     //       MODIFIED       na
    1534             :     //       RE-ENGINEERED  na
    1535             : 
    1536             :     // PURPOSE OF THIS FUNCTION:
    1537             :     // Provides a clean way to quickly bound a generic value to within any two other values
    1538             : 
    1539             :     // METHODOLOGY EMPLOYED:
    1540             :     // Bound up to min and down to max
    1541             : 
    1542             :     // Return value
    1543             :     Real64 BoundedValue;
    1544             : 
    1545    21304399 :     BoundedValue = ValueToBound;
    1546    21304399 :     BoundedValue = max(BoundedValue, LowerBound);
    1547    21304399 :     BoundedValue = min(BoundedValue, UpperBound);
    1548             : 
    1549    21304399 :     return BoundedValue;
    1550             : }
    1551             : 
    1552           0 : bool IntegerIsWithinTwoValues(int const ValueToCheck, int const LowerBound, int const UpperBound)
    1553             : {
    1554             : 
    1555             :     // FUNCTION INFORMATION:
    1556             :     //       AUTHOR         Edwin Lee
    1557             :     //       DATE WRITTEN   September 2010
    1558             :     //       MODIFIED       na
    1559             :     //       RE-ENGINEERED  na
    1560             : 
    1561             :     // PURPOSE OF THIS FUNCTION:
    1562             :     // Provides a clean way to quickly check if an integer is within two values
    1563             : 
    1564             :     // METHODOLOGY EMPLOYED:
    1565             :     // TRUE if ValueToCheck = [LowerBound, UpperBound]
    1566             :     // in other words, it returns true if ValueToCheck=LowerBound, or if ValueToCheck=UpperBound
    1567             : 
    1568             :     // Return value
    1569           0 :     return (ValueToCheck >= LowerBound) && (ValueToCheck <= UpperBound);
    1570             : }
    1571             : 
    1572             : // In-Place Right Shift by 1 of Array Elements
    1573    61617352 : void rshift1(Array1D<Real64> &a, Real64 const a_l)
    1574             : {
    1575    61617352 :     assert(a.size_bounded());
    1576   308086760 :     for (int i = a.u(), e = a.l(); i > e; --i) {
    1577   246469408 :         a(i) = a(i - 1);
    1578             :     }
    1579    61617352 :     a(a.l()) = a_l;
    1580    61617352 : }
    1581             : 
    1582     2944078 : void LogPlantConvergencePoints(EnergyPlusData &state, bool const FirstHVACIteration)
    1583             : {
    1584             : 
    1585             :     // SUBROUTINE INFORMATION:
    1586             :     //       AUTHOR         Edwin Lee
    1587             :     //       DATE WRITTEN   Summer 2011
    1588             :     //       MODIFIED       na
    1589             :     //       RE-ENGINEERED  na
    1590             : 
    1591             :     // PURPOSE OF THIS SUBROUTINE:
    1592             :     // This routine stores the history of the plant convergence to check for stuck (max iteration) conditions
    1593             : 
    1594             :     // METHODOLOGY EMPLOYED:
    1595             :     // Loop across all loops and loopsides
    1596             :     //   On first hvac, reset the history arrays to begin anew
    1597             :     //   Pick up the LoopSide inlet and outlet temp and flow rate
    1598             :     //   Store this in the history array of each node using EOSHIFT
    1599             : 
    1600    10646247 :     for (int ThisLoopNum = 1; ThisLoopNum <= isize(state.dataPlnt->PlantLoop); ++ThisLoopNum) {
    1601     7702169 :         auto &loop = state.dataPlnt->PlantLoop(ThisLoopNum);
    1602    23106507 :         for (DataPlant::LoopSideLocation ThisLoopSide : DataPlant::LoopSideKeys) {
    1603    15404338 :             auto &loop_side = loop.LoopSide(ThisLoopSide);
    1604             : 
    1605    15404338 :             if (FirstHVACIteration) {
    1606     7455662 :                 loop_side.InletNode.TemperatureHistory = 0.0;
    1607     7455662 :                 loop_side.InletNode.MassFlowRateHistory = 0.0;
    1608     7455662 :                 loop_side.OutletNode.TemperatureHistory = 0.0;
    1609     7455662 :                 loop_side.OutletNode.MassFlowRateHistory = 0.0;
    1610             :             }
    1611             : 
    1612    15404338 :             int InletNodeNum = loop_side.NodeNumIn;
    1613    15404338 :             Real64 InletNodeTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
    1614    15404338 :             Real64 InletNodeMdot = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
    1615             : 
    1616    15404338 :             int OutletNodeNum = loop_side.NodeNumOut;
    1617    15404338 :             Real64 OutletNodeTemp = state.dataLoopNodes->Node(OutletNodeNum).Temp;
    1618    15404338 :             Real64 OutletNodeMdot = state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate;
    1619             : 
    1620    15404338 :             rshift1(loop_side.InletNode.TemperatureHistory, InletNodeTemp);
    1621    15404338 :             rshift1(loop_side.InletNode.MassFlowRateHistory, InletNodeMdot);
    1622    15404338 :             rshift1(loop_side.OutletNode.TemperatureHistory, OutletNodeTemp);
    1623    15404338 :             rshift1(loop_side.OutletNode.MassFlowRateHistory, OutletNodeMdot);
    1624             :         }
    1625             :     }
    1626     2944078 : }
    1627             : 
    1628       17654 : void ScanPlantLoopsForObject(EnergyPlusData &state,
    1629             :                              std::string_view CompName,
    1630             :                              DataPlant::PlantEquipmentType CompType,
    1631             :                              PlantLocation &plantLoc,
    1632             :                              bool &errFlag,
    1633             :                              ObjexxFCL::Optional<Real64 const> LowLimitTemp,
    1634             :                              ObjexxFCL::Optional<Real64 const> HighLimitTemp,
    1635             :                              ObjexxFCL::Optional_int CountMatchPlantLoops,
    1636             :                              ObjexxFCL::Optional_int_const InletNodeNumber,
    1637             :                              ObjexxFCL::Optional_int_const SingleLoopSearch,
    1638             :                              ObjexxFCL::Optional_bool_const suppressErrors)
    1639             : {
    1640             : 
    1641             :     // SUBROUTINE INFORMATION:
    1642             :     //       AUTHOR         Edwin Lee
    1643             :     //       DATE WRITTEN   November 2009
    1644             :     //       MODIFIED       B. Griffith, changes to help with single component one multiple plant loops
    1645             :     //       RE-ENGINEERED  na
    1646             :     // PURPOSE OF THIS SUBROUTINE:
    1647             :     // This subroutine scans the plant loop structure trying to find the component by type then name.
    1648             :     // If there are more than one match, it counts them up and returns count using an optional output arg
    1649             :     // If the option input declaring the component inlet's node name, then the matching is more specific.
    1650             :     // An optional input, lowlimittemp, can be passed in to be used in the PlantCondLoopOperation routines
    1651             :     //  when distributing loads to components
    1652             :     // METHODOLOGY EMPLOYED:
    1653             :     // Standard EnergyPlus methodology.
    1654             : 
    1655             :     // Using/Aliasing
    1656             :     using BranchInputManager::AuditBranches;
    1657             : 
    1658             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1659             :     int LoopCtr;
    1660             :     int BranchCtr;
    1661             :     int CompCtr;
    1662             :     bool FoundComponent;
    1663             :     int FoundCount;
    1664             :     bool FoundCompName;
    1665             :     int StartingLoopNum;
    1666             :     int EndingLoopNum;
    1667             : 
    1668       17654 :     FoundCount = 0;
    1669             : 
    1670       17654 :     FoundComponent = false;
    1671       17654 :     FoundCompName = false;
    1672       17654 :     StartingLoopNum = 1;
    1673       17654 :     EndingLoopNum = state.dataPlnt->TotNumLoops;
    1674       17654 :     if (present(SingleLoopSearch)) {
    1675        1635 :         StartingLoopNum = SingleLoopSearch;
    1676        1635 :         EndingLoopNum = SingleLoopSearch;
    1677             :     }
    1678             : 
    1679       68982 :     for (LoopCtr = StartingLoopNum; LoopCtr <= EndingLoopNum; ++LoopCtr) {
    1680       51328 :         auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
    1681      153984 :         for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
    1682      102656 :             auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
    1683     1089498 :             for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
    1684      986842 :                 auto &this_branch = this_loop_side.Branch(BranchCtr);
    1685     1978338 :                 for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
    1686      991496 :                     auto &this_component = this_branch.Comp(CompCtr);
    1687      991496 :                     if (this_component.Type == CompType) {
    1688      338746 :                         if (Util::SameString(CompName, this_component.Name)) {
    1689       18782 :                             FoundCompName = true;
    1690       18782 :                             if (present(InletNodeNumber)) {
    1691        2410 :                                 if (InletNodeNumber > 0) {
    1692             :                                     // check if inlet nodes agree
    1693        2410 :                                     if (InletNodeNumber == this_component.NodeNumIn) {
    1694        1284 :                                         FoundComponent = true;
    1695        1284 :                                         ++FoundCount;
    1696        1284 :                                         plantLoc.loopNum = LoopCtr;
    1697        1284 :                                         plantLoc.loopSideNum = LoopSideCtr;
    1698        1284 :                                         plantLoc.branchNum = BranchCtr;
    1699        1284 :                                         plantLoc.compNum = CompCtr;
    1700             :                                     }
    1701             :                                 }
    1702             :                             } else {
    1703       16372 :                                 FoundComponent = true;
    1704       16372 :                                 ++FoundCount;
    1705       16372 :                                 plantLoc.loopNum = LoopCtr;
    1706       16372 :                                 plantLoc.loopSideNum = LoopSideCtr;
    1707       16372 :                                 plantLoc.branchNum = BranchCtr;
    1708       16372 :                                 plantLoc.compNum = CompCtr;
    1709             :                             }
    1710       18782 :                             if (present(LowLimitTemp)) {
    1711         604 :                                 this_component.MinOutletTemp = LowLimitTemp;
    1712             :                             }
    1713       18782 :                             if (present(HighLimitTemp)) {
    1714         208 :                                 this_component.MaxOutletTemp = HighLimitTemp;
    1715             :                             }
    1716             :                         }
    1717             :                     }
    1718             :                 }
    1719             :             }
    1720             :         }
    1721             :     }
    1722             : 
    1723       17654 :     bool skipErrors = false;
    1724       17654 :     if (present(suppressErrors)) {
    1725           0 :         skipErrors = suppressErrors;
    1726             :     }
    1727             : 
    1728       17654 :     if (!FoundComponent && !skipErrors) {
    1729           0 :         if (CompType != DataPlant::PlantEquipmentType::Invalid && CompType != DataPlant::PlantEquipmentType::Num) {
    1730           0 :             if (!present(SingleLoopSearch)) {
    1731           0 :                 ShowSevereError(state,
    1732           0 :                                 format("Plant Component {} called \"{}\" was not found on any plant loops.",
    1733           0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)],
    1734           0 :                                        std::string{CompName}));
    1735           0 :                 AuditBranches(state, true, DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)], CompName);
    1736             :             } else {
    1737           0 :                 ShowSevereError(state,
    1738           0 :                                 format("Plant Component {} called \"{}\" was not found on plant loop=\"{}\".",
    1739           0 :                                        DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)],
    1740           0 :                                        std::string{CompName},
    1741           0 :                                        state.dataPlnt->PlantLoop(SingleLoopSearch).Name));
    1742             :             }
    1743           0 :             if (present(InletNodeNumber)) {
    1744           0 :                 if (FoundCompName) {
    1745           0 :                     ShowContinueError(state, format("Looking for matching inlet Node=\"{}\".", state.dataLoopNodes->NodeID(InletNodeNumber)));
    1746             :                 }
    1747             :             }
    1748           0 :             if (present(SingleLoopSearch)) {
    1749           0 :                 ShowContinueError(state, format("Look at Operation Scheme=\"{}\".", state.dataPlnt->PlantLoop(SingleLoopSearch).OperationScheme));
    1750           0 :                 ShowContinueError(state, "Look at Branches and Components on the Loop.");
    1751           0 :                 ShowBranchesOnLoop(state, SingleLoopSearch);
    1752             :             }
    1753           0 :             errFlag = true;
    1754             :         } else {
    1755           0 :             ShowSevereError(state, format("ScanPlantLoopsForObject: Invalid CompType passed [{}], Name={}", CompType, CompName));
    1756           0 :             ShowContinueError(state, format("Valid CompTypes are in the range [0 - {}].", static_cast<int>(DataPlant::PlantEquipmentType::Num)));
    1757           0 :             ShowFatalError(state, "Previous error causes program termination");
    1758             :         }
    1759             :     }
    1760             : 
    1761       17654 :     if (present(CountMatchPlantLoops)) {
    1762        7193 :         CountMatchPlantLoops = FoundCount;
    1763             :     }
    1764       17654 : }
    1765             : 
    1766        9692 : void ScanPlantLoopsForNodeNum(EnergyPlusData &state,
    1767             :                               std::string_view const CallerName, // really used for error messages
    1768             :                               int const NodeNum,                 // index in Node structure of node to be scanned
    1769             :                               PlantLocation &pLantLoc,           // return value for location
    1770             :                               ObjexxFCL::Optional_int CompNum)
    1771             : {
    1772             : 
    1773             :     // SUBROUTINE INFORMATION:
    1774             :     //       AUTHOR         B. Griffith
    1775             :     //       DATE WRITTEN   Feb. 2010
    1776             :     //       MODIFIED       na
    1777             :     //       RE-ENGINEERED  na
    1778             : 
    1779             :     // PURPOSE OF THIS SUBROUTINE:
    1780             :     // Get routine to return plant loop index and plant loop side
    1781             :     // based on node number.  for one time init routines only.
    1782             : 
    1783             :     // METHODOLOGY EMPLOYED:
    1784             :     // Loop thru plant data structure and find matching node.
    1785             : 
    1786             :     int LoopCtr;
    1787             :     int BranchCtr;
    1788             :     int CompCtr;
    1789             :     bool FoundNode;
    1790             :     int inFoundCount;
    1791             :     int outFoundCount;
    1792             : 
    1793        9692 :     inFoundCount = 0;
    1794        9692 :     outFoundCount = 0;
    1795        9692 :     if (present(CompNum)) {
    1796           2 :         CompNum = 0;
    1797             :     }
    1798        9692 :     FoundNode = false;
    1799             : 
    1800       41896 :     for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
    1801       32204 :         auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
    1802       96612 :         for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
    1803       64408 :             auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
    1804      921276 :             for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
    1805      856868 :                 auto &this_branch = this_loop_side.Branch(BranchCtr);
    1806     1717164 :                 for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
    1807      860296 :                     auto &this_comp = this_branch.Comp(CompCtr);
    1808      860296 :                     if (NodeNum == this_comp.NodeNumIn) {
    1809        9692 :                         FoundNode = true;
    1810        9692 :                         ++inFoundCount;
    1811        9692 :                         pLantLoc.loopNum = LoopCtr;
    1812        9692 :                         pLantLoc.loopSideNum = LoopSideCtr;
    1813        9692 :                         pLantLoc.branchNum = BranchCtr;
    1814        9692 :                         if (present(CompNum)) {
    1815           2 :                             CompNum = CompCtr;
    1816             :                         }
    1817             :                     }
    1818             : 
    1819      860296 :                     if (NodeNum == this_comp.NodeNumOut) {
    1820           2 :                         ++outFoundCount;
    1821           2 :                         pLantLoc.loopNum = LoopCtr;
    1822           2 :                         pLantLoc.loopSideNum = LoopSideCtr;
    1823           2 :                         pLantLoc.branchNum = BranchCtr;
    1824             :                     }
    1825             :                 }
    1826             :             }
    1827             :         }
    1828             :     }
    1829             : 
    1830        9692 :     if (!FoundNode) {
    1831           0 :         ShowSevereError(state, "ScanPlantLoopsForNodeNum: Plant Node was not found as inlet node (for component) on any plant loops");
    1832           0 :         ShowContinueError(state, format("Node Name=\"{}\"", state.dataLoopNodes->NodeID(NodeNum)));
    1833           0 :         if (!state.dataGlobal->DoingSizing) {
    1834           0 :             ShowContinueError(state, format("called by {}", CallerName));
    1835             :         } else {
    1836           0 :             ShowContinueError(state, format("during sizing: called by {}", CallerName));
    1837             :         }
    1838           0 :         if (outFoundCount > 0) ShowContinueError(state, format("Node was found as outlet node (for component) {} time(s).", outFoundCount));
    1839           0 :         ShowContinueError(state, "Possible error in Branch inputs.  For more information, look for other error messages related to this node name.");
    1840             :         // fatal?
    1841             :     }
    1842        9692 : }
    1843             : 
    1844      283632 : bool AnyPlantLoopSidesNeedSim(EnergyPlusData &state)
    1845             : {
    1846             : 
    1847             :     // FUNCTION INFORMATION:
    1848             :     //       AUTHOR         Edwin Lee
    1849             :     //       DATE WRITTEN   November 2009
    1850             :     //       MODIFIED       na
    1851             :     //       RE-ENGINEERED  na
    1852             :     // PURPOSE OF THIS FUNCTION:
    1853             :     // This subroutine scans the plant LoopSide simflags and returns if any of them are still true
    1854             : 
    1855             :     // Return value
    1856             :     bool AnyPlantLoopSidesNeedSim;
    1857             : 
    1858             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1859             :     int LoopCtr;
    1860             : 
    1861             :     // Assume that there aren't any
    1862      283632 :     AnyPlantLoopSidesNeedSim = false;
    1863             : 
    1864             :     // Then check if there are any
    1865      503688 :     for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
    1866      706576 :         for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
    1867      486520 :             if (state.dataPlnt->PlantLoop(LoopCtr).LoopSide(LoopSideCtr).SimLoopSideNeeded) {
    1868       46408 :                 AnyPlantLoopSidesNeedSim = true;
    1869       46408 :                 return AnyPlantLoopSidesNeedSim;
    1870             :             }
    1871             :         }
    1872             :     }
    1873             : 
    1874      237224 :     return AnyPlantLoopSidesNeedSim;
    1875             : }
    1876             : 
    1877     6707170 : void SetAllPlantSimFlagsToValue(EnergyPlusData &state, bool const Value)
    1878             : {
    1879             : 
    1880             :     // SUBROUTINE INFORMATION:
    1881             :     //       AUTHOR         Edwin Lee
    1882             :     //       DATE WRITTEN   November 2009
    1883             :     //       MODIFIED       na
    1884             :     //       RE-ENGINEERED  B. Griffith Feb 2009
    1885             :     // PURPOSE OF THIS SUBROUTINE:
    1886             :     // Quickly sets all sim flags of a certain type (loop type/side) to a value
    1887             : 
    1888             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1889             :     int LoopCtr;
    1890             : 
    1891             :     // Loop over all loops
    1892    16057551 :     for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
    1893     9350381 :         auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
    1894     9350381 :         this_loop.LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = Value;
    1895     9350381 :         this_loop.LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = Value;
    1896             :     }
    1897     6707170 : }
    1898             : 
    1899           0 : void ShowBranchesOnLoop(EnergyPlusData &state, int const LoopNum) // Loop number of loop
    1900             : {
    1901             : 
    1902             :     // SUBROUTINE INFORMATION:
    1903             :     //       AUTHOR         Linda Lawrie
    1904             :     //       DATE WRITTEN   November 2011
    1905             :     //       MODIFIED       na
    1906             :     //       RE-ENGINEERED  na
    1907             : 
    1908             :     // PURPOSE OF THIS SUBROUTINE:
    1909             :     // This routine will display (with continue error messages) the branch/component
    1910             :     // structure of the given loop.
    1911             : 
    1912             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1913             :     int BrN; // Branch counter
    1914             :     int CpN; // Component (on branch) counter
    1915             : 
    1916           0 :     for (DataPlant::LoopSideLocation LSN : DataPlant::LoopSideKeys) {
    1917           0 :         ShowContinueError(state, format("{} Branches:", DataPlant::DemandSupplyNames[static_cast<int>(LSN)]));
    1918           0 :         for (BrN = 1; BrN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).TotalBranches; ++BrN) {
    1919           0 :             ShowContinueError(state, format("  {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Name));
    1920           0 :             ShowContinueError(state, "    Components on Branch:");
    1921           0 :             for (CpN = 1; CpN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).TotalComponents; ++CpN) {
    1922           0 :                 ShowContinueError(state,
    1923           0 :                                   format("      {}:{}",
    1924           0 :                                          state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).TypeOf,
    1925           0 :                                          state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).Name));
    1926             :             }
    1927             :         }
    1928             :     }
    1929           0 : }
    1930             : 
    1931        8824 : int MyPlantSizingIndex(EnergyPlusData &state,
    1932             :                        std::string_view const CompType,       // component description
    1933             :                        std::string_view const CompName,       // user name of component
    1934             :                        int const NodeNumIn,                   // component water inlet node
    1935             :                        [[maybe_unused]] int const NodeNumOut, // component water outlet node
    1936             :                        bool &ErrorsFound,                     // set to true if there's an error, unchanged otherwise
    1937             :                        bool const PrintErrorFlag              // used for WSHP's where condenser loop may not be on a plant loop
    1938             : )
    1939             : {
    1940             : 
    1941             :     // FUNCTION INFORMATION:
    1942             :     //       AUTHOR         Fred Buhl
    1943             :     //       DATE WRITTEN   July 2008
    1944             :     //       MODIFIED       na
    1945             :     //       RE-ENGINEERED  na
    1946             : 
    1947             :     // PURPOSE OF THIS FUNCTION:
    1948             :     // Identify the correct Plant Sizing object for demand-side components such as heating and
    1949             :     // cooling coils.
    1950             : 
    1951             :     // METHODOLOGY EMPLOYED:
    1952             :     // This function searches all plant loops for a component whose input and
    1953             :     // output nodes match the desired input & output nodes. This plant loop index is then used
    1954             :     // to search the Plant Sizing array for the matching Plant Sizing object.
    1955             : 
    1956             :     // Using/Aliasing
    1957             :     using DataSizing::PlantSizingData;
    1958             : 
    1959             :     // Return value
    1960             :     int MyPltSizNum; // returned plant sizing index
    1961             : 
    1962        8824 :     int MyPltLoopNum{};
    1963        8824 :     PlantLocation DummyPlantLoc{};
    1964             : 
    1965        8824 :     MyPltSizNum = 0;
    1966             : 
    1967        8824 :     ScanPlantLoopsForNodeNum(state, "MyPlantSizingIndex", NodeNumIn, DummyPlantLoc);
    1968             : 
    1969        8824 :     if (DummyPlantLoc.loopNum > 0) {
    1970        8824 :         MyPltLoopNum = DummyPlantLoc.loopNum;
    1971             :     } else {
    1972           0 :         MyPltLoopNum = 0;
    1973             :     }
    1974             : 
    1975        8824 :     if (MyPltLoopNum > 0) {
    1976        8824 :         if (state.dataSize->NumPltSizInput > 0) {
    1977             :             MyPltSizNum =
    1978        8824 :                 Util::FindItemInList(state.dataPlnt->PlantLoop(MyPltLoopNum).Name, state.dataSize->PlantSizData, &PlantSizingData::PlantLoopName);
    1979             :         }
    1980        8824 :         if (MyPltSizNum == 0) {
    1981          15 :             if (PrintErrorFlag) {
    1982          30 :                 ShowSevereError(
    1983          30 :                     state, format("MyPlantSizingIndex: Could not find {} in Sizing:Plant objects.", state.dataPlnt->PlantLoop(MyPltLoopNum).Name));
    1984          15 :                 ShowContinueError(state, format("...reference Component Type=\"{}\", Name=\"{}\".", CompType, CompName));
    1985             :             }
    1986          15 :             ErrorsFound = true;
    1987             :         }
    1988             :     } else {
    1989           0 :         if (PrintErrorFlag) {
    1990           0 :             ShowWarningError(state, format("MyPlantSizingIndex: Could not find {} with name {} on any plant loop", CompType, CompName));
    1991             :         }
    1992           0 :         ErrorsFound = true;
    1993             :     }
    1994             : 
    1995        8824 :     return MyPltSizNum;
    1996             : }
    1997             : 
    1998           4 : bool verifyTwoNodeNumsOnSamePlantLoop(EnergyPlusData &state, int const nodeIndexA, int const nodeIndexB)
    1999             : {
    2000             :     // this function simply searches across plant loops looking for node numbers
    2001             :     // it returns true if the two nodes are found to be on the same loop
    2002             :     // it returns false otherwise
    2003             :     // because this is a nested loop, there's no reason it should be called except in one-time fashion
    2004           4 :     int matchedIndexA = 0;
    2005           4 :     int matchedIndexB = 0;
    2006          14 :     for (int loopNum = 1; loopNum <= state.dataPlnt->TotNumLoops; loopNum++) {
    2007          30 :         for (auto &loopSide : state.dataPlnt->PlantLoop(loopNum).LoopSide) {
    2008         144 :             for (auto &branch : loopSide.Branch) {
    2009         248 :                 for (auto &comp : branch.Comp) {
    2010         124 :                     if (comp.NodeNumIn == nodeIndexA || comp.NodeNumOut == nodeIndexA) {
    2011           4 :                         matchedIndexA = loopNum;
    2012             :                     }
    2013         124 :                     if (comp.NodeNumIn == nodeIndexB || comp.NodeNumOut == nodeIndexB) {
    2014           4 :                         matchedIndexB = loopNum;
    2015             :                     }
    2016             :                 }
    2017             :             }
    2018             :         }
    2019             :     }
    2020           4 :     return (matchedIndexA == matchedIndexB) && (matchedIndexA != 0); // only return true if both are equal and non-zero
    2021             : }
    2022             : 
    2023             : Real64
    2024     1445569 : MinFlowIfBranchHasVSPump(EnergyPlusData &state, PlantLocation const &plantLoc, bool &foundBranchPump, bool &foundLoopPump, bool const setFlowStatus)
    2025             : {
    2026     1445569 :     Real64 branchPumpMinFlowLimit = 0.0;
    2027             : 
    2028     1445569 :     int NumCompsOnThisBranch = state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum).Branch(plantLoc.branchNum).TotalComponents;
    2029     2891142 :     for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
    2030     1445575 :         auto &component(state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum).Branch(plantLoc.branchNum).Comp(CompCounter));
    2031     1445575 :         if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
    2032     1445573 :             component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
    2033           2 :             foundBranchPump = true;
    2034           2 :             if (component.CompNum > 0) branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
    2035           2 :             break;
    2036             :         }
    2037             :     }
    2038             : 
    2039     1445569 :     if (!foundBranchPump) {
    2040             :         // second, if no branch pump, search for variable speed pump on inlet branch of supply side of this loop
    2041     1445567 :         if (state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches > 1) {
    2042             :             int NumCompsOnInletBranch =
    2043     1445567 :                 state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents;
    2044     2029828 :             for (int CompCounter = 1; CompCounter <= NumCompsOnInletBranch; ++CompCounter) {
    2045             :                 auto &component(
    2046     1445571 :                     state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(CompCounter));
    2047     1445571 :                 if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
    2048      736336 :                     component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
    2049      861310 :                     foundLoopPump = true;
    2050      861310 :                     if (component.CompNum > 0) branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
    2051      861310 :                     break;
    2052             :                 }
    2053             :             }
    2054             :         }
    2055             :     }
    2056             : 
    2057     1445569 :     if (setFlowStatus) {
    2058          14 :         if (branchPumpMinFlowLimit > 0.0 && foundBranchPump) {
    2059           0 :             state.dataPlnt->PlantLoop(plantLoc.loopNum)
    2060           0 :                 .LoopSide(plantLoc.loopSideNum)
    2061           0 :                 .Branch(plantLoc.branchNum)
    2062           0 :                 .Comp(plantLoc.compNum)
    2063           0 :                 .FlowPriority = DataPlant::LoopFlowStatus::NeedyIfLoopOn;
    2064             :         } else {
    2065          14 :             state.dataPlnt->PlantLoop(plantLoc.loopNum)
    2066          14 :                 .LoopSide(plantLoc.loopSideNum)
    2067          14 :                 .Branch(plantLoc.branchNum)
    2068          14 :                 .Comp(plantLoc.compNum)
    2069          14 :                 .FlowPriority = DataPlant::LoopFlowStatus::TakesWhatGets;
    2070             :         }
    2071             :     }
    2072             : 
    2073     1445569 :     return branchPumpMinFlowLimit;
    2074             : }
    2075             : 
    2076             : } // namespace EnergyPlus::PlantUtilities

Generated by: LCOV version 1.14