LCOV - code coverage report
Current view: top level - EnergyPlus - PlantUtilities.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 559 794 70.4 %
Date: 2023-01-17 19:17:23 Functions: 30 32 93.8 %

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

Generated by: LCOV version 1.13