LCOV - code coverage report
Current view: top level - EnergyPlus - PlantUtilities.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 69.0 % 843 582
Test Date: 2025-05-22 16:09:37 Functions: 96.9 % 32 31

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

Generated by: LCOV version 2.0-1