LCOV - code coverage report
Current view: top level - EnergyPlus/Plant - LoopSide.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 810 962 84.2 %
Date: 2024-08-23 23:50:59 Functions: 20 20 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : #include <ObjexxFCL/Fmath.hh>
      49             : #include <ObjexxFCL/member.functions.hh>
      50             : 
      51             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      52             : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
      53             : #include <EnergyPlus/DataConvergParams.hh>
      54             : #include <EnergyPlus/DataHVACGlobals.hh>
      55             : #include <EnergyPlus/FluidProperties.hh>
      56             : #include <EnergyPlus/General.hh>
      57             : #include <EnergyPlus/HVACInterfaceManager.hh>
      58             : #include <EnergyPlus/Plant/DataPlant.hh>
      59             : #include <EnergyPlus/Plant/LoopSide.hh>
      60             : #include <EnergyPlus/PlantCondLoopOperation.hh>
      61             : #include <EnergyPlus/PlantPressureSystem.hh>
      62             : #include <EnergyPlus/PlantUtilities.hh>
      63             : #include <EnergyPlus/Pumps.hh>
      64             : #include <EnergyPlus/UtilityRoutines.hh>
      65             : 
      66             : namespace EnergyPlus {
      67             : namespace DataPlant {
      68             : 
      69             :     static constexpr std::string_view fluidNameSteam("STEAM");
      70             : 
      71    36200510 :     void HalfLoopData::solve(EnergyPlusData &state, bool const FirstHVACIteration, bool &ReSimOtherSideNeeded)
      72             :     {
      73             : 
      74             :         // SUBROUTINE INFORMATION:
      75             :         //       AUTHORS:         Dan Fisher, Sankaranarayanan K P, Edwin Lee
      76             :         //       DATE WRITTEN:    April 1998
      77             :         //       MODIFIED         June 2005(Work in the Plant Super Manager Module)
      78             :         //                        July 2006
      79             :         //       RE-ENGINEERED    July 2010
      80             : 
      81             :         // PURPOSE OF THIS SUBROUTINE:
      82             :         // SimSupplyFlowSolution is the driver routine for plant loops.  It performs
      83             :         //  the following tasks for each half loop (supply or demand side):
      84             :         // 1. Calculate flow request for half loop
      85             :         // 2. Predict Loop Flow
      86             :         // 3. Simulate the inlet branch
      87             :         // 4. Simulate the parallel branches, distributing load if necessary
      88             :         // 5. Set flow rates on parallel branches
      89             :         // 6. Simulate outlet branch and update node and report variables
      90             : 
      91             :         // METHODOLOGY EMPLOYED:
      92             :         // The algorithm operates on a predictor/corrector flow setting method by simulating all available loop components
      93             :         // based on component requested flow rates, then enforcing continuity on all loop branch flows by calling
      94             :         // the flow resolver and locking those flows down.  Available components are then re-simulated using the
      95             :         // corrected flow rates.
      96             : 
      97    36200510 :         auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
      98    36200510 :         int ThisSideInletNode = this->NodeNumIn;
      99             : 
     100    36200510 :         this->InitialDemandToLoopSetPoint = 0.0;
     101    36200510 :         this->CurrentAlterationsToDemand = 0.0;
     102    36200510 :         this->UpdatedDemandToLoopSetPoint = 0.0;
     103             : 
     104             :         // The following block is related to validating the flow control paths of the loop side
     105             :         // Since the control types are scheduled, I think BeginTimeStep should be a decent check frequency
     106    36200510 :         if (state.dataGlobal->BeginTimeStepFlag && this->OncePerTimeStepOperations) {
     107             : 
     108             :             // Initialize loop side controls -- could just be done for one loop since this routine inherently
     109             :             //  loops over all plant/condenser loops.  Not sure if the penalty is worth investigating.
     110    12187666 :             PlantCondLoopOperation::InitLoadDistribution(state, FirstHVACIteration);
     111             : 
     112             :             // Now that the op scheme types are updated, do LoopSide validation
     113    12187666 :             this->ValidateFlowControlPaths(state);
     114             : 
     115             :             // Set the flag to false so we won't do these again this time step
     116    12187666 :             this->OncePerTimeStepOperations = false;
     117             : 
     118             :         } else {
     119             : 
     120             :             // Set the flag to true so that it is activated for the next time step
     121    24012844 :             this->OncePerTimeStepOperations = true;
     122             :         }
     123             : 
     124             :         // Do pressure system initialize if this is the demand side (therefore once per whole loop)
     125    36200510 :         if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
     126    18105180 :             PlantPressureSystem::SimPressureDropSystem(state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Init);
     127             :         }
     128             : 
     129             :         // Turn on any previously disabled branches due to constant speed branch pump issue
     130    36200510 :         this->TurnOnAllLoopSideBranches();
     131             : 
     132    36200510 :         LoopSideLocation OtherLoopSide = static_cast<LoopSideLocation>(LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)]);
     133             :         // Do the actual simulation here every time
     134    36200510 :         this->DoFlowAndLoadSolutionPass(state, OtherLoopSide, ThisSideInletNode, FirstHVACIteration);
     135             : 
     136             :         // On constant speed branch pump loop sides we need to re-simulate
     137    36200510 :         if (this->hasConstSpeedBranchPumps) {
     138             :             // turn off any pumps connected to unloaded equipment and re-do the flow/load solution pass
     139       85431 :             this->DisableAnyBranchPumpsConnectedToUnloadedEquipment();
     140       85431 :             this->DoFlowAndLoadSolutionPass(state, OtherLoopSide, ThisSideInletNode, FirstHVACIteration);
     141             :         }
     142             : 
     143             :         // A couple things are specific to which LoopSide we are on  // TODO: This whole block needs to be moved up to the loop level
     144    36200510 :         if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
     145             : 
     146             :             // Pass the loop information via the HVAC interface manager
     147    54315540 :             HVACInterfaceManager::UpdatePlantLoopInterface(state,
     148    18105180 :                                                            this->plantLoc,
     149    18105180 :                                                            thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut,
     150    18105180 :                                                            thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn,
     151             :                                                            ReSimOtherSideNeeded,
     152             :                                                            thisPlantLoop.CommonPipeType);
     153             : 
     154             :         } else { // LoopSide == LoopSideLocation::Supply
     155             : 
     156             :             // Update pressure drop reporting, calculate total loop pressure drop for use elsewhere
     157    18095330 :             PlantPressureSystem::SimPressureDropSystem(state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Update);
     158             : 
     159             :             // Pass the loop information via the HVAC interface manager (only the flow)
     160    54285990 :             HVACInterfaceManager::UpdatePlantLoopInterface(state,
     161    18095330 :                                                            this->plantLoc,
     162    18095330 :                                                            thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut,
     163    18095330 :                                                            thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn,
     164             :                                                            ReSimOtherSideNeeded,
     165             :                                                            thisPlantLoop.CommonPipeType);
     166             : 
     167             :             // Update the loop outlet node conditions
     168    18095330 :             state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
     169    18095330 :                 .CheckLoopExitNode(state, FirstHVACIteration); // TODO: This is a loop level check, move out
     170             : 
     171    18095330 :             state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
     172    18095330 :                 .UpdateLoopSideReportVars(state, this->InitialDemandToLoopSetPointSAVED, this->LoadToLoopSetPointThatWasntMet);
     173             :         }
     174    36200510 :     }
     175             : 
     176    12187666 :     void HalfLoopData::ValidateFlowControlPaths(EnergyPlusData &state)
     177             :     {
     178             : 
     179             :         // FUNCTION INFORMATION:
     180             :         //       AUTHOR         Edwin Lee
     181             :         //       DATE WRITTEN   July 2010
     182             :         //       MODIFIED       na
     183             :         //       RE-ENGINEERED  na
     184             : 
     185             :         // PURPOSE OF THIS FUNCTION:
     186             :         // This routine will scan all the loop side paths and validate the component topology according
     187             :         //  to current topology rules and regulations.
     188             : 
     189             :         // METHODOLOGY EMPLOYED:
     190             :         // Scan this loop side and begin by scanning the first branch, then follow with the remainder of the flow paths
     191             :         //  - this would be from splitter outlet nodes all the way to the loop side outlet node.
     192             :         // The current rules are that "other types" of components (as defined below in the references) can be placed along each
     193             :         //  flow path as needed.  At this point, any number of "load-range based" components can be placed along the flow
     194             :         //  path.  After this, the user is allowed to place another set of any number of "other types" of components.
     195             :         // The key restriction is that an "other type" of component may not be sandwiched by "load-range based" components.
     196             :         // This is due to the load range based needing to be simulated all at once along each flow path.
     197             : 
     198             :         // REFERENCES:
     199             :         // "other types" of components: basically not load-range based heat transfer components.  This would include:
     200             :         //    - demand based components such as coils
     201             :         //    - component setpoint based operating components
     202             :         //    - heat exchanger components including waterside economizers
     203             :         // "load-range based" components are heat transfer components which are controlled based on a single load range.
     204             :         //    - currently only one load range based scheme is available at a given time, although other control types
     205             :         //      may be enabled, such as component setpoint.
     206             :         // Pumps are separate components since the pump heat is not accounted for in the flow path order.
     207             :         //  Improvements during the demand side rewrite has allowed pumps to be placed as -not- the first component on a branch
     208             :         //  Pumps can be placed anywhere, even between load-range based components, since they will not affect loop load
     209             : 
     210             :         // RETURN VALUE:
     211             :         // Returns a control validator flow structure, including a flag for successful or not, then if not successful
     212             :         //  the other values are filled in such as location on the loop where the error occurred and a message error description
     213             : 
     214             :         // FUNCTION PARAMETER DEFINITIONS:
     215    12187666 :         int constexpr Parallel(1);
     216    12187666 :         int constexpr Outlet(2);
     217             : 
     218             :         //~ Initialze
     219    12187666 :         bool EncounteredLRB = false;
     220    12187666 :         bool EncounteredNonLRBAfterLRB = false;
     221    12187666 :         int const NumParallelPaths = this->TotalBranches - 2;
     222             : 
     223             :         // We'll start by stepping through the first branch, which may be the only branch
     224             :         // If we find a load range based, then trip the flag and just keep moving
     225             :         // If we only have one branch and all is good, then RETURN early
     226             :         // If we have parallel branches, then start looping through each flow path to
     227             :         //  decide if it is a valid path.
     228             :         // If any one path is invalid then all is wrong
     229    12187666 :         int firstBranchIndex = 1;
     230    24580420 :         for (int CompIndex = 1; CompIndex <= this->Branch(firstBranchIndex).TotalComponents; ++CompIndex) {
     231             : 
     232    12392754 :             auto const &this_component(this->Branch(firstBranchIndex).Comp(CompIndex));
     233             : 
     234             :             {
     235    12392754 :                 switch (this_component.CurOpSchemeType) {
     236        2872 :                 case OpScheme::HeatingRB:
     237             :                 case OpScheme::CoolingRB: { //~ load range based
     238        2872 :                     if (EncounteredNonLRBAfterLRB) {
     239             :                         // We must have already encountered a LRB, then a non-LRB, and now another LRB, this is bad
     240           0 :                         ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
     241           0 :                         ShowContinueError(state, "PlaLoad range based components are separated by other control type components.");
     242           0 :                         ShowContinueError(state, "Load Range Based should be grouped together on each flow path.");
     243           0 :                         ShowFatalError(state, "Plant topology issue causes program termination");
     244             :                     } else {
     245        2872 :                         EncounteredLRB = true;
     246             :                     }
     247        2872 :                     break;
     248             :                 }
     249     6179905 :                 case DataPlant::OpScheme::Pump: { //~ pump
     250             :                     // For now this is just a placeholder, because I think pumps will be available anywhere,
     251             :                     //  and they won't affect the load distribution
     252     6179905 :                     break;
     253             :                 }
     254     6125106 :                 case DataPlant::OpScheme::NoControl: { //~ Such as pipes
     255             :                     // For now this is just a placeholder, because these components shouldn't cause a problem anywhere...
     256     6125106 :                     break;
     257             :                 }
     258           0 :                 case DataPlant::OpScheme::Invalid: { //~ Uninitialized, this should be a sufficient place to catch for this on branch 1
     259             :                     // throw fatal
     260           0 :                     ShowSevereError(state,
     261           0 :                                     "ValidateFlowControlPaths: Uninitialized operation scheme type for component Name: " + this_component.Name);
     262           0 :                     ShowFatalError(state, "ValidateFlowControlPaths: developer notice, Inlet path validation loop");
     263           0 :                     break;
     264             :                 }
     265       84871 :                 default: { //~ Other control type
     266       84871 :                     if (EncounteredLRB) {
     267           0 :                         EncounteredNonLRBAfterLRB = true;
     268             :                     } else {
     269             :                         // For now don't do anything, but we'll see...
     270             :                     }
     271             :                 }
     272             :                 }
     273             :             }
     274             :         }
     275             : 
     276             :         // Return early if we only needed to do the one branch
     277    12187666 :         if (NumParallelPaths <= 0) return;
     278             : 
     279             :         // Now, if we have multiple parallel branches, I think the easiest way is to go all the way from the inlet node
     280             :         //  of each parallel branch to the loop outlet node and check the flow path
     281             :         // This way we don't have to remember the conditions on each of the parallel branches when we would finally move
     282             :         //  to analyzing the outlet node when all done
     283             :         // This will reduce allocation on the heap because we will keep from storing that array
     284             :         // For each parallel path, we will need to check two branches: the parallel branch and the LoopSide outlet branch
     285    65114993 :         for (int PathCounter = 1; PathCounter <= NumParallelPaths; ++PathCounter) {
     286   158995035 :             for (int ParallelOrOutletIndex = Parallel; ParallelOrOutletIndex <= Outlet; ++ParallelOrOutletIndex) {
     287             :                 int BranchIndex;
     288   105996690 :                 if (ParallelOrOutletIndex == Parallel) {
     289             :                     // The branch index will be the current pathtype + 1 to add the inlet branch
     290    52998345 :                     BranchIndex = PathCounter + 1;
     291             :                 } else { // ParallelOrOutletIndex == Outlet
     292             :                     // The branch index will be the LoopSide outlet node
     293    52998345 :                     BranchIndex = this->TotalBranches;
     294             :                 }
     295             : 
     296             :                 // Now that we have the branch index, let's do the control type check over all the components
     297   212411022 :                 for (int CompIndex = 1; CompIndex <= this->Branch(BranchIndex).TotalComponents; ++CompIndex) {
     298             : 
     299   106414332 :                     auto const &this_component(this->Branch(BranchIndex).Comp(CompIndex));
     300             : 
     301             :                     {
     302   106414332 :                         switch (this_component.CurOpSchemeType) {
     303     6464652 :                         case OpScheme::HeatingRB:
     304             :                         case OpScheme::CoolingRB: { //~ load range based
     305     6464652 :                             if (EncounteredNonLRBAfterLRB) {
     306             :                                 // We must have already encountered a LRB, then a non-LRB, and now another LRB, this is bad
     307           0 :                                 ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
     308           0 :                                 ShowContinueError(state, "Load range based components are separated by other control type components.");
     309           0 :                                 ShowContinueError(state, "Load Range Based should be grouped together on each flow path.");
     310           0 :                                 ShowFatalError(state, "Plant topology issue causes program termination");
     311             :                             } else {
     312     6464652 :                                 EncounteredLRB = true;
     313             :                             }
     314     6464652 :                             break;
     315             :                         }
     316             : 
     317    64691972 :                         case DataPlant::OpScheme::NoControl: { //~ Such as pipes
     318             :                             // For now this is just a placeholder, because these components shouldn't cause a problem anywhere...
     319    64691972 :                             break;
     320             :                         }
     321       90330 :                         case DataPlant::OpScheme::Pump: { //~ pump
     322             :                             // For now this is just a placeholder, because I think pumps will be available anywhere,
     323             :                             //  and they won't affect the load distribution
     324       90330 :                             break;
     325             :                         }
     326           0 :                         case DataPlant::OpScheme::Invalid: { //~ Uninitialized, this should be sufficient place to
     327             :                                                              // catch for this on other branches
     328             :                             // throw fatal error
     329           0 :                             ShowSevereError(
     330           0 :                                 state, "ValidateFlowControlPaths: Uninitialized operation scheme type for component Name: " + this_component.Name);
     331           0 :                             ShowFatalError(state, "ValidateFlowControlPaths: developer notice, problem in Parallel path validation loop");
     332           0 :                             break;
     333             :                         }
     334    35167378 :                         default: { //~ Other control type
     335    35167378 :                             if (EncounteredLRB) {
     336           0 :                                 EncounteredNonLRBAfterLRB = true;
     337             :                             } else {
     338             :                                 // For now don't do anything, but we'll see...
     339             :                             }
     340             :                         }
     341             :                         }
     342             :                     }
     343             : 
     344             :                 } //~ CompIndex
     345             : 
     346             :             } //~ Parallel and Outlet Branches
     347             : 
     348             :         } //~ Parallel Paths
     349             :     }
     350             : 
     351   113875800 :     bool HalfLoopData::CheckPlantConvergence(bool const FirstHVACIteration)
     352             :     {
     353             : 
     354             :         // FUNCTION INFORMATION:
     355             :         //       AUTHOR         Edwin Lee
     356             :         //       DATE WRITTEN   Summer 2011
     357             :         //       MODIFIED       na
     358             :         //       RE-ENGINEERED  na
     359             : 
     360             :         // PURPOSE OF THIS FUNCTION:
     361             :         // This routine checks the history values in the convergence arrays of this loop/LoopSide combination
     362             : 
     363             :         // METHODOLOGY EMPLOYED:
     364             :         // On FirstHVAC, we are not converged yet, thus forcing at least two iterations
     365             :         // Calculate the average of each related variable history (generalized: could be any number of history terms)
     366             :         // If any of the history terms do not match this average, then at least one value is different, so not converged
     367             :         // Although this routine appears to check for convergence, it is also used to check for stuck (max iteration) conditions
     368             :         //  in cases where demand side (air loop, for example) equipment is "fighting" with the plant loop
     369             :         // The result of this routine can help the plant "lock-in" and take action to stop the iteration
     370             : 
     371             :         // Using/Aliasing
     372             :         using namespace DataPlant;
     373             :         using namespace DataLoopNode;
     374             : 
     375             :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
     376             :         Real64 InletAvgTemp;
     377             :         Real64 InletAvgMdot;
     378             :         Real64 OutletAvgTemp;
     379             :         Real64 OutletAvgMdot;
     380             : 
     381   113875800 :         if (FirstHVACIteration) {
     382    50485122 :             return false;
     383             :         }
     384             : 
     385    63390678 :         InletAvgTemp = sum(this->InletNode.TemperatureHistory) / size(this->InletNode.TemperatureHistory);
     386    63390678 :         if (any_ne(this->InletNode.TemperatureHistory, InletAvgTemp)) {
     387    62964601 :             return false;
     388             :         }
     389             : 
     390      426077 :         InletAvgMdot = sum(this->InletNode.MassFlowRateHistory) / size(this->InletNode.MassFlowRateHistory);
     391      426077 :         if (any_ne(this->InletNode.MassFlowRateHistory, InletAvgMdot)) {
     392      173944 :             return false;
     393             :         }
     394             : 
     395      252133 :         OutletAvgTemp = sum(this->OutletNode.TemperatureHistory) / size(this->OutletNode.TemperatureHistory);
     396      252133 :         if (any_ne(this->OutletNode.TemperatureHistory, OutletAvgTemp)) {
     397       77055 :             return false;
     398             :         }
     399             : 
     400      175078 :         OutletAvgMdot = sum(this->OutletNode.MassFlowRateHistory) / size(this->OutletNode.MassFlowRateHistory);
     401      175078 :         if (any_ne(this->OutletNode.MassFlowRateHistory, OutletAvgMdot)) {
     402        6870 :             return false;
     403             :         }
     404             : 
     405             :         // If we made it this far, we're good!
     406      168208 :         return true;
     407             :     }
     408             : 
     409   113875800 :     void HalfLoopData::PushBranchFlowCharacteristics(EnergyPlusData &state,
     410             :                                                      int const BranchNum,
     411             :                                                      Real64 const ValueToPush,
     412             :                                                      bool const FirstHVACIteration // TRUE if First HVAC iteration of Time step
     413             :     )
     414             :     {
     415             : 
     416             :         // SUBROUTINE INFORMATION:
     417             :         //       AUTHOR         Edwin Lee
     418             :         //       DATE WRITTEN   September 2010
     419             :         //       MODIFIED       na
     420             :         //       RE-ENGINEERED  na
     421             : 
     422             :         // PURPOSE OF THIS SUBROUTINE:
     423             :         // This routine takes the flow resolved flow rate and pushes it
     424             :         //  down a branch.  In the process, if an externally connected
     425             :         //  component (air-water coil for example) is found to have a
     426             :         //  differing flow rate, the air sim flag is tripped to true, but
     427             :         //  the flow resolved flow rate is pushed down the loop to allow
     428             :         //  the plant to finish successfully.
     429             : 
     430             :         // METHODOLOGY EMPLOYED:
     431             :         // Push mass flow rate and max avail down each branch.  If the component
     432             :         //  is connected (or could be, for now) to an external loop such as
     433             :         //  an air loop, the current component outlet mass flow is checked
     434             :         //  vs the current resolved mass flow.  If the mass flow doesn't match,
     435             :         //  the air sim flag is tripped to true.
     436             : 
     437             :         // Currently this routine is only performed for starved branches, when
     438             :         //  the coil is requesting too much flow, more than the plant can provide.
     439             :         // If this were moved to every call type, including a minimum plant flow,
     440             :         //  you would need to provide a mass flow and min/max avail to push
     441             :         //  down the branch as well.
     442             : 
     443             :         // Using/Aliasing
     444             :         using namespace DataPlant; // Use the entire module to allow all TypeOf's, would be a huge ONLY list
     445             : 
     446             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     447             :         int CompCounter;
     448             :         int BranchInletNode;
     449             :         DataPlant::PlantEquipmentType ComponentType;
     450             :         Real64 MassFlowRateFound;
     451             :         Real64 MassFlow;
     452             :         bool PlantIsRigid;
     453             : 
     454   113875800 :         auto &this_branch(this->Branch(BranchNum));
     455             : 
     456   113875800 :         BranchInletNode = this_branch.NodeNumIn;
     457             : 
     458             :         //~ Possible error handling if needed
     459   113875800 :         if (ValueToPush != state.dataLoopNodes->Node(BranchInletNode).MassFlowRate) {
     460             :             // Diagnostic problem, flow resolver isn't calling this routine properly
     461             :         }
     462             : 
     463             :         //~ This section would really be useful more later on if this routine has more logic regarding what to push down the branch
     464   113875800 :         MassFlow = ValueToPush;
     465             :         // MinAvail = ValueToPush
     466             :         // MaxAvail = ValueToPush
     467             : 
     468   113875800 :         PlantIsRigid = this->CheckPlantConvergence(FirstHVACIteration);
     469             : 
     470             :         //~ Loop across all component outlet nodes and update their mass flow and max avail
     471   228359972 :         for (CompCounter = 1; CompCounter <= this_branch.TotalComponents; ++CompCounter) {
     472             : 
     473   114484172 :             auto const &this_comp(this_branch.Comp(CompCounter));
     474             : 
     475             :             //~ Pick up some values for convenience
     476   114484172 :             int ComponentInletNode = this_comp.NodeNumIn;
     477   114484172 :             int ComponentOutletNode = this_comp.NodeNumOut;
     478   114484172 :             MassFlowRateFound = state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate;
     479   114484172 :             ComponentType = this_comp.Type;
     480             : 
     481             :             //~ Push the values through
     482   114484172 :             state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate = MassFlow;
     483             : 
     484   114484172 :             if (PlantIsRigid) {
     485      169328 :                 state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMinAvail = MassFlow;
     486      169328 :                 state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMaxAvail = MassFlow;
     487      169328 :                 state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMinAvail = MassFlow;
     488      169328 :                 state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMaxAvail = MassFlow;
     489             :             }
     490             :             // Node(ComponentOutletNode)%MassFlowRateMinAvail = MinAvail
     491             :             // no this is 2-way valve which messes up flow options
     492             :             //      for demand components Node(ComponentOutletNode)%MassFlowRateMaxAvail = MaxAvail
     493             : 
     494             :             //~ If this value matches then we are good to move to the next component
     495   114484172 :             if (std::abs(MassFlow - MassFlowRateFound) < CriteriaDelta_MassFlowRate) continue;
     496             :             //~ Since there is a difference, we have to decide what to do based on the component type:
     497             :             //~  For plant connections, don't do anything, it SHOULD work itself out
     498             :             //~  For air connections, trip the LoopSide air flag
     499             :             //~  Similar for zone, none zone, and electric load center
     500             :             {
     501    12058625 :                 switch (ComponentType) {
     502             : 
     503             :                     // possibly air-connected components
     504      858487 :                 case DataPlant::PlantEquipmentType::CoilWaterCooling:
     505             :                 case DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling:
     506             :                 case DataPlant::PlantEquipmentType::CoilWaterSimpleHeating:
     507             :                 case DataPlant::PlantEquipmentType::CoilSteamAirHeating:
     508             :                 case DataPlant::PlantEquipmentType::CoilWAHPHeatingEquationFit:
     509             :                 case DataPlant::PlantEquipmentType::CoilWAHPCoolingEquationFit:
     510             :                 case DataPlant::PlantEquipmentType::CoilWAHPHeatingParamEst:
     511             :                 case DataPlant::PlantEquipmentType::CoilWAHPCoolingParamEst:
     512             :                 case DataPlant::PlantEquipmentType::CoilUserDefined:
     513             :                 case DataPlant::PlantEquipmentType::CoilVSWAHPCoolingEquationFit:
     514             :                 case DataPlant::PlantEquipmentType::CoilVSWAHPHeatingEquationFit:
     515             :                 case DataPlant::PlantEquipmentType::PackagedTESCoolingCoil: {
     516      858487 :                     this->SimAirLoopsNeeded = true;
     517             :                     // sometimes these coils are children in ZoneHVAC equipment
     518             :                     // PlantLoop(LoopNum)%LoopSide(LoopSideNum)%SimZoneEquipNeeded= .TRUE.
     519      858487 :                     break;
     520             :                 }
     521             : 
     522             :                     // zone connected components
     523       24458 :                 case DataPlant::PlantEquipmentType::CoolingPanel_Simple:
     524             :                 case DataPlant::PlantEquipmentType::Baseboard_Conv_Water:
     525             :                 case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Steam:
     526             :                 case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Water:
     527             :                 case DataPlant::PlantEquipmentType::LowTempRadiant_VarFlow:
     528             :                 case DataPlant::PlantEquipmentType::LowTempRadiant_ConstFlow:
     529             :                 case DataPlant::PlantEquipmentType::CooledBeamAirTerminal:
     530             :                 case DataPlant::PlantEquipmentType::ZoneHVACAirUserDefined:
     531             :                 case DataPlant::PlantEquipmentType::AirTerminalUserDefined:
     532             :                 case DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal: {
     533       24458 :                     this->SimZoneEquipNeeded = true;
     534       24458 :                     break;
     535             :                 }
     536             : 
     537             :                 // electric center connected components
     538        5751 :                 case DataPlant::PlantEquipmentType::Generator_FCExhaust:
     539             :                 case DataPlant::PlantEquipmentType::Generator_FCStackCooler:
     540             :                 case DataPlant::PlantEquipmentType::Generator_MicroCHP:
     541             :                 case DataPlant::PlantEquipmentType::Generator_MicroTurbine:
     542             :                 case DataPlant::PlantEquipmentType::Generator_ICEngine:
     543             :                 case DataPlant::PlantEquipmentType::Generator_CTurbine: {
     544        5751 :                     this->SimElectLoadCentrNeeded = true;
     545        5751 :                     break;
     546             :                 }
     547             : 
     548    11169929 :                 default:
     549    11169929 :                     break;
     550             :                 }
     551             :             }
     552             :         }
     553   113875800 :     }
     554             : 
     555    36200510 :     void HalfLoopData::TurnOnAllLoopSideBranches()
     556             :     {
     557   194186306 :         for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
     558   157985796 :             auto &branch = this->Branch(branchNum);
     559   157985796 :             branch.disableOverrideForCSBranchPumping = false;
     560             :         }
     561    36200510 :     }
     562             : 
     563    72571882 :     void HalfLoopData::SimulateAllLoopSideBranches(EnergyPlusData &state,
     564             :                                                    Real64 const ThisLoopSideFlow,
     565             :                                                    bool const FirstHVACIteration,
     566             :                                                    bool &LoopShutDownFlag)
     567             :     {
     568             : 
     569             :         // SUBROUTINE INFORMATION:
     570             :         //       AUTHOR         Edwin Lee
     571             :         //       DATE WRITTEN   July 2010
     572             :         //       MODIFIED       na
     573             :         //       RE-ENGINEERED  na
     574             : 
     575             :         // PURPOSE OF THIS SUBROUTINE:
     576             :         // This routine will step through all branch groups (single branch .OR. inlet/parallels/outlet)
     577             :         //  and call the branch group simulation routine.  This routine also calls to update the splitter
     578             :         //  and mixer.
     579             : 
     580             :         // METHODOLOGY EMPLOYED:
     581             :         // The number of branch groups is either 1 or 3.  1 would be a single branch half-loop.  3 would
     582             :         //  be the minimum for an inlet/parallels/outlet set.  The number of branch groups can then be
     583             :         //  calculated as #BrGrps = 1 + 2*L; where L is zero for single half loop and one for parallel-type set.
     584             :         //  This calculation can be reduced to the logical/integer conversion as shown in the code.
     585             :         // The simulation then steps through each branch group.  If there are parallel branches, the splitter is
     586             :         //  updated on flowlock=0 to pass information through, then after the parallel branches the mixer is always
     587             :         //  updated.  The outlet branch "group" is then simulated.
     588             : 
     589             :         // SUBROUTINE PARAMETER DEFINITIONS:
     590    72571882 :         int constexpr InletBranchOrOneBranchHalfLoop(1);
     591    72571882 :         int constexpr ParallelBranchSet(2);
     592    72571882 :         int constexpr OutletBranch(3);
     593             : 
     594    72571882 :         int NumBranchGroups = 1;
     595    72571882 :         if (this->TotalBranches > 1) {
     596    72250166 :             NumBranchGroups = 3;
     597             :         }
     598             : 
     599             :         // reset branch starting component index back to zero before each pass
     600   533856184 :         for (int BranchCounter = 1; BranchCounter <= this->TotalBranches; ++BranchCounter) {
     601   461284302 :             this->Branch(BranchCounter).lastComponentSimulated = 0;
     602             :         }
     603             : 
     604   289644096 :         for (int BranchGroup = 1; BranchGroup <= NumBranchGroups; ++BranchGroup) {
     605             : 
     606   217072214 :             if ((BranchGroup > 1) && (this->TotalBranches == 1)) break;
     607             : 
     608   217072214 :             switch (BranchGroup) {
     609    72571882 :             case InletBranchOrOneBranchHalfLoop:
     610    72571882 :                 this->SimulateLoopSideBranchGroup(state, 1, 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
     611    72571882 :                 break;
     612    72250166 :             case ParallelBranchSet:
     613    72250166 :                 this->UpdatePlantSplitter(state);
     614    72250166 :                 this->SimulateLoopSideBranchGroup(state, 2, this->TotalBranches - 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
     615    72250166 :                 this->UpdatePlantMixer(state);
     616    72250166 :                 break;
     617    72250166 :             case OutletBranch:
     618    72250166 :                 this->SimulateLoopSideBranchGroup(
     619             :                     state, this->TotalBranches, this->TotalBranches, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
     620    72250166 :                 break;
     621             :             }
     622             :         }
     623    72571882 :     }
     624             : 
     625    56107969 :     void HalfLoopData::AdjustPumpFlowRequestByEMSControls(int const BranchNum, int const CompNum, Real64 &FlowToRequest)
     626             :     {
     627             : 
     628             :         // SUBROUTINE INFORMATION:
     629             :         //       AUTHOR         Brent Griffith
     630             :         //       DATE WRITTEN   April 2012
     631             :         //       MODIFIED       na
     632             :         //       RE-ENGINEERED  na
     633             : 
     634             :         // PURPOSE OF THIS SUBROUTINE:
     635             :         // modify flow request to pump simulation if EMS is overriding pump component
     636             : 
     637             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     638    56107969 :         auto &this_branch(this->Branch(BranchNum));
     639    56107969 :         auto &this_comp(this_branch.Comp(CompNum));
     640             : 
     641    56107969 :         if ((this->EMSCtrl) && (this->EMSValue <= 0.0)) {
     642           0 :             FlowToRequest = 0.0;
     643           0 :             return;
     644             :         }
     645             : 
     646    56107969 :         if ((this_branch.EMSCtrlOverrideOn) && (this_branch.EMSCtrlOverrideValue <= 0.0)) {
     647           0 :             FlowToRequest = 0.0;
     648           0 :             return;
     649             :         }
     650             : 
     651    56107969 :         if (this_comp.EMSLoadOverrideOn) {
     652      334139 :             if (this_comp.EMSLoadOverrideValue == 0.0) {
     653      253511 :                 FlowToRequest = 0.0;
     654             :             }
     655             :         }
     656             :     }
     657             : 
     658       85431 :     void HalfLoopData::DisableAnyBranchPumpsConnectedToUnloadedEquipment()
     659             :     {
     660      330762 :         for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
     661      245331 :             auto &branch = this->Branch(branchNum);
     662      245331 :             Real64 totalDispatchedLoadOnBranch = 0.0;
     663      661524 :             for (int compNum = 1; compNum <= branch.TotalComponents; ++compNum) {
     664      416193 :                 auto const &component = branch.Comp(compNum);
     665      416193 :                 auto const &t = component.Type;
     666      416193 :                 if (t == DataPlant::PlantEquipmentType::PumpConstantSpeed || t == DataPlant::PlantEquipmentType::PumpBankConstantSpeed ||
     667      245331 :                     t == DataPlant::PlantEquipmentType::PumpVariableSpeed || t == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
     668             :                     // don't do anything
     669             :                 } else {
     670      245331 :                     totalDispatchedLoadOnBranch += component.MyLoad;
     671             :                 }
     672             :             }
     673      245331 :             if (std::abs(totalDispatchedLoadOnBranch) < 0.001) {
     674      192042 :                 branch.disableOverrideForCSBranchPumping = true;
     675             :             }
     676             :         }
     677       85431 :     }
     678             : 
     679    36285941 :     Real64 HalfLoopData::EvaluateLoopSetPointLoad(EnergyPlusData &state, int const FirstBranchNum, int const LastBranchNum, Real64 ThisLoopSideFlow)
     680             :     {
     681             : 
     682             :         // FUNCTION INFORMATION:
     683             :         //       AUTHOR         Edwin Lee
     684             :         //       DATE WRITTEN   August 2010
     685             :         //       MODIFIED       na
     686             :         //       RE-ENGINEERED  na
     687             : 
     688             :         // Return value
     689    36285941 :         Real64 LoadToLoopSetPoint = 0.0; // function result
     690             : 
     691             :         static constexpr std::string_view RoutineName("PlantLoopSolver::EvaluateLoopSetPointLoad");
     692             :         static constexpr std::string_view RoutineNameAlt("PlantSupplySide:EvaluateLoopSetPointLoad");
     693             : 
     694             :         //~ General variables
     695    36285941 :         Real64 SumMdotTimesTemp = 0.0;
     696    36285941 :         Real64 SumMdot = 0.0;
     697             : 
     698    36285941 :         auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
     699             : 
     700             :         // We will place one specialized case in here for common pipe simulations.
     701             :         // If we are doing a common pipe simulation, and there is greater other-side flow than this side,
     702             :         //  then the "other side" demand needs to include getting the flow through the common pipe to the same setpoint
     703             :         //  as the flow going through the actual supply side
     704    36285941 :         if (this->hasConstSpeedBranchPumps && this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply &&
     705      120503 :             thisPlantLoop.CommonPipeType != DataPlant::CommonPipeType::No) {
     706       19955 :             const DataPlant::LoopSideLocation OtherSide = LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)];
     707       19955 :             const int otherSideOutletNodeNum = thisPlantLoop.LoopSide(OtherSide).NodeNumOut;
     708       19955 :             Real64 commonPipeFlow = state.dataLoopNodes->Node(otherSideOutletNodeNum).MassFlowRate - ThisLoopSideFlow;
     709       19955 :             Real64 otherSideExitingTemperature = state.dataLoopNodes->Node(otherSideOutletNodeNum).Temp;
     710       19955 :             SumMdotTimesTemp += otherSideExitingTemperature * commonPipeFlow;
     711       19955 :             SumMdot += commonPipeFlow;
     712             :         }
     713             : 
     714             :         // Sweep across flow paths in this group and calculate the deltaT and then the load
     715    36285941 :         int BranchIndex = 0; // ~ This is a 1 - n value within the current branch group
     716    72571882 :         for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
     717             : 
     718    36285941 :             ++BranchIndex;
     719             : 
     720             :             //~ Always start from the last component we did the last time around + 1 and
     721             :             //~  try to make it all the way to the end of the loop
     722    36285941 :             int StartingComponent = this->Branch(BranchCounter).lastComponentSimulated + 1;
     723    36285941 :             int EnteringNodeNum = this->Branch(BranchCounter).Comp(StartingComponent).NodeNumIn;
     724             : 
     725    36285941 :             Real64 EnteringTemperature = state.dataLoopNodes->Node(EnteringNodeNum).Temp;
     726    36285941 :             Real64 MassFlowRate = state.dataLoopNodes->Node(EnteringNodeNum).MassFlowRate;
     727             : 
     728    36285941 :             SumMdotTimesTemp += EnteringTemperature * MassFlowRate;
     729    36285941 :             SumMdot += MassFlowRate;
     730             :         }
     731             : 
     732    36285941 :         if (SumMdot < DataBranchAirLoopPlant::MassFlowTolerance) {
     733    16977386 :             return 0.0;
     734             :         }
     735             : 
     736    19308555 :         Real64 WeightedInletTemp = SumMdotTimesTemp / SumMdot;
     737             : 
     738    19308555 :         if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Water) {
     739             : 
     740             :             Real64 Cp =
     741    19270517 :                 FluidProperties::GetSpecificHeatGlycol(state, thisPlantLoop.FluidName, WeightedInletTemp, thisPlantLoop.FluidIndex, RoutineName);
     742             : 
     743             :             {
     744             : 
     745    19270517 :                 if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     746             : 
     747             :                     // Pick up the loop setpoint temperature
     748    18929009 :                     Real64 LoopSetPointTemperature = this->TempSetPoint;
     749             :                     // Calculate the delta temperature
     750    18929009 :                     Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
     751             : 
     752             :                     // Calculate the demand on the loop
     753    18929009 :                     LoadToLoopSetPoint = SumMdot * Cp * DeltaTemp;
     754             : 
     755      341508 :                 } else if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
     756             : 
     757             :                     // Get the range of setpoints
     758      341508 :                     Real64 LoopSetPointTemperatureHi = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointHi;
     759      341508 :                     Real64 LoopSetPointTemperatureLo = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointLo;
     760             : 
     761             :                     // Calculate the demand on the loop
     762      341508 :                     if (SumMdot > 0.0) {
     763      341508 :                         Real64 LoadToHeatingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureLo - WeightedInletTemp);
     764      341508 :                         Real64 LoadToCoolingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureHi - WeightedInletTemp);
     765             :                         // Possible combinations:
     766             :                         // 1  LoadToHeatingSetPoint > 0 & LoadToCoolingSetPoint > 0 -->  Heating required
     767             :                         // 2  LoadToHeatingSetPoint < 0 & LoadToCoolingSetPoint < 0 -->  Cooling Required
     768             :                         // 3  LoadToHeatingSetPoint <=0 & LoadToCoolingSetPoint >=0 -->  Dead Band Operation - includes zero load cases
     769             :                         // 4  LoadToHeatingSetPoint  >  LoadToCoolingSetPoint       -->  Not Feasible if LoopSetPointHi >= LoopSetPointLo
     770             :                         // First trap bad set-points
     771      341508 :                         if (LoadToHeatingSetPoint > LoadToCoolingSetPoint) {
     772           0 :                             ShowSevereError(state,
     773             :                                             "Plant Loop: the Plant Loop Demand Calculation Scheme is set to DualSetPointDeadBand, but the "
     774             :                                             "heating-related low setpoint appears to be above the cooling-related high setpoint.");
     775           0 :                             ShowContinueError(state,
     776             :                                               "For example, if using SetpointManager:Scheduled:DualSetpoint, then check that the low setpoint is "
     777             :                                               "below the high setpoint.");
     778           0 :                             ShowContinueError(state, "Occurs in PlantLoop=" + thisPlantLoop.Name);
     779           0 :                             ShowContinueError(
     780             :                                 state,
     781           0 :                                 format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
     782           0 :                             ShowContinueError(state, format("Loop Heating Low Setpoint={:.2R}", LoopSetPointTemperatureLo));
     783           0 :                             ShowContinueError(state, format("Loop Cooling High Setpoint={:.2R}", LoopSetPointTemperatureHi));
     784             : 
     785           0 :                             ShowFatalError(state, "Program terminates due to above conditions.");
     786             :                         }
     787      341508 :                         if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
     788       86954 :                             LoadToLoopSetPoint = LoadToHeatingSetPoint;
     789      254554 :                         } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
     790      147108 :                             LoadToLoopSetPoint = LoadToCoolingSetPoint;
     791      107446 :                         } else if (LoadToHeatingSetPoint <= 0.0 && LoadToCoolingSetPoint >= 0.0) { // deadband includes zero loads
     792      107446 :                             LoadToLoopSetPoint = 0.0;
     793             :                         } else {
     794           0 :                             ShowSevereError(state,
     795             :                                             "DualSetPointWithDeadBand: Unanticipated combination of heating and cooling loads - report to EnergyPlus "
     796             :                                             "Development Team");
     797           0 :                             ShowContinueError(state, "occurs in PlantLoop=" + thisPlantLoop.Name);
     798           0 :                             ShowContinueError(
     799             :                                 state,
     800           0 :                                 format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
     801           0 :                             ShowContinueError(state, format("Loop Heating Setpoint={:.2R}", LoopSetPointTemperatureLo));
     802           0 :                             ShowContinueError(state, format("Loop Cooling Setpoint={:.2R}", LoopSetPointTemperatureHi));
     803           0 :                             ShowFatalError(state, "Program terminates due to above conditions.");
     804             :                         }
     805             :                     } else {
     806           0 :                         LoadToLoopSetPoint = 0.0;
     807             :                     }
     808             :                 }
     809             :             }
     810             : 
     811       38038 :         } else if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Steam) {
     812             : 
     813             :             Real64 Cp =
     814       38038 :                 FluidProperties::GetSpecificHeatGlycol(state, thisPlantLoop.FluidName, WeightedInletTemp, thisPlantLoop.FluidIndex, RoutineName);
     815             : 
     816             :             {
     817             : 
     818       38038 :                 if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     819             : 
     820             :                     // Pick up the loop setpoint temperature
     821       38038 :                     Real64 LoopSetPointTemperature = this->TempSetPoint;
     822             : 
     823             :                     // Calculate the delta temperature
     824       38038 :                     Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
     825             : 
     826             :                     Real64 EnthalpySteamSatVapor =
     827       38038 :                         FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, LoopSetPointTemperature, 1.0, this->refrigIndex, RoutineNameAlt);
     828             :                     Real64 EnthalpySteamSatLiquid =
     829       38038 :                         FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, LoopSetPointTemperature, 0.0, this->refrigIndex, RoutineNameAlt);
     830             : 
     831       38038 :                     Real64 LatentHeatSteam = EnthalpySteamSatVapor - EnthalpySteamSatLiquid;
     832             : 
     833             :                     // Calculate the demand on the loop
     834       38038 :                     LoadToLoopSetPoint = SumMdot * (Cp * DeltaTemp + LatentHeatSteam);
     835             :                 }
     836             :             }
     837             : 
     838             :         } else { // only have two types, water serves for glycol.
     839             :         }
     840             : 
     841             :         // Trim the demand to zero if it is very small
     842    19308555 :         if (std::abs(LoadToLoopSetPoint) < DataPlant::LoopDemandTol) LoadToLoopSetPoint = 0.0;
     843             : 
     844    19308555 :         return LoadToLoopSetPoint;
     845             :     }
     846             : 
     847    36285941 :     Real64 HalfLoopData::CalcOtherSideDemand(EnergyPlusData &state, Real64 ThisLoopSideFlow)
     848             :     {
     849             : 
     850             :         // FUNCTION INFORMATION:
     851             :         //       AUTHOR         Edwin Lee
     852             :         //       DATE WRITTEN   August 2010
     853             :         //       MODIFIED       na
     854             :         //       RE-ENGINEERED  na
     855             : 
     856             :         // PURPOSE OF THIS FUNCTION:
     857             :         // To evaluate the demand to hit the loop setpoint based on the loop side inlet conditions
     858             : 
     859             :         // METHODOLOGY EMPLOYED:
     860             :         // This routine will simply call the evaluate loop setpoint routine but call it from
     861             :         //  the very beginning of this loop side, so that it is basically for the entire loop side
     862             : 
     863             :         // FUNCTION PARAMETER DEFINITIONS:
     864    36285941 :         return this->EvaluateLoopSetPointLoad(state, 1, 1, ThisLoopSideFlow);
     865             :     }
     866             : 
     867    36285941 :     Real64 HalfLoopData::SetupLoopFlowRequest(EnergyPlusData &state, const LoopSideLocation OtherSide)
     868             :     {
     869             : 
     870             :         // FUNCTION INFORMATION:
     871             :         //       AUTHOR:          Dan Fisher, Edwin Lee
     872             :         //       DATE WRITTEN:    August 2010
     873             :         //       MODIFIED:        na
     874             :         //       RE-ENGINEERED:   na
     875             : 
     876             :         // PURPOSE OF THIS SUBROUTINE:
     877             :         // This routine sets up the flow request values and sums them up for each loop side
     878             :         // Then makes a decision on the desired loop flow based on loop configuration
     879             : 
     880             :         // METHODOLOGY EMPLOYED:
     881             :         // Scan through the components on this loop side, and look at the mass flow request
     882             :         //  values on components inlet node.
     883             :         // Check common pipe/pumping configuration for this loop side and the other loop side
     884             :         //  to determine what the LoopSide should flow
     885             : 
     886             :         //~ Initialize
     887    36285941 :         Real64 LoopFlow = 0.0; // Once all flow requests are evaluated, this is the desired flow on this side
     888             : 
     889             :         // reference
     890    36285941 :         auto &loop(state.dataPlnt->PlantLoop(this->plantLoc.loopNum));
     891             : 
     892             :         //~ First we need to set up the flow requests on each LoopSide
     893   108857823 :         for (DataPlant::LoopSideLocation LoopSideCounter : DataPlant::LoopSideKeys) {
     894             :             // Clear things out for this LoopSide
     895    72571882 :             Real64 InletBranchRequestNeedAndTurnOn = 0.0;
     896    72571882 :             Real64 InletBranchRequestNeedIfOn = 0.0;
     897    72571882 :             Real64 ParallelBranchRequestsNeedAndTurnOn(0.0);
     898    72571882 :             Real64 ParallelBranchRequestsNeedIfOn(0.0);
     899    72571882 :             Real64 OutletBranchRequestNeedAndTurnOn = 0.0;
     900    72571882 :             Real64 OutletBranchRequestNeedIfOn = 0.0;
     901             : 
     902             :             // reference
     903    72571882 :             auto &loop_side(loop.LoopSide(LoopSideCounter));
     904             : 
     905    72571882 :             loop_side.flowRequestNeedIfOn = 0.0;
     906    72571882 :             loop_side.flowRequestNeedAndTurnOn = 0.0;
     907    72571882 :             loop_side.flowRequestFinal = 0.0;
     908    72571882 :             loop_side.hasConstSpeedBranchPumps = false;
     909             : 
     910             :             // Now loop through all the branches on this LoopSide and get flow requests
     911    72571882 :             int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
     912    72571882 :             int ParallelBranchIndex = 0;
     913   533743694 :             for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
     914   461171812 :                 Real64 ThisBranchFlowRequestNeedAndTurnOn = 0.0;
     915   461171812 :                 Real64 ThisBranchFlowRequestNeedIfOn = 0.0;
     916             : 
     917             :                 // reference
     918   461171812 :                 auto &branch(loop_side.Branch(BranchCounter));
     919             : 
     920   461171812 :                 if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) ++ParallelBranchIndex;
     921             : 
     922   461171812 :                 if (branch.disableOverrideForCSBranchPumping) {
     923      384072 :                     branch.RequestedMassFlow = 0.0;
     924      384072 :                     continue;
     925             :                 }
     926             : 
     927   460787740 :                 int const NumCompsOnThisBranch = branch.TotalComponents;
     928   924477192 :                 for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
     929             : 
     930             :                     // reference
     931   463689452 :                     auto &component(branch.Comp(CompCounter));
     932             : 
     933   463689452 :                     int NodeToCheckRequest = component.NodeNumIn;
     934   463689452 :                     LoopFlowStatus FlowPriorityStatus = component.FlowPriority;
     935             : 
     936             :                     // reference
     937   463689452 :                     auto &node_with_request(state.dataLoopNodes->Node(NodeToCheckRequest));
     938             : 
     939   463689452 :                     if (!DataPlant::PlantEquipmentTypeIsPump[static_cast<int>(component.Type)]) {
     940             : 
     941   426670137 :                         if (FlowPriorityStatus == DataPlant::LoopFlowStatus::Invalid) {
     942             :                             // do nothing
     943   426655601 :                         } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyAndTurnsLoopOn) {
     944   203031851 :                             ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, node_with_request.MassFlowRateRequest);
     945   203031851 :                             ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
     946   223623750 :                         } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyIfLoopOn) {
     947    20595438 :                             ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
     948             :                         } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::TakesWhatGets) {
     949             :                             // do nothing
     950             :                         }
     951             :                     } else { // handle pumps differently
     952    37019315 :                         if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
     953    35866166 :                             (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay)) {
     954             :                             // special primary side flow request for two way common pipe
     955      409178 :                             int const CompIndex = component.CompNum;
     956      409178 :                             switch (component.Type) {
     957             :                             // remove var speed pumps from this case statement if can set MassFlowRateRequest
     958      409178 :                             case DataPlant::PlantEquipmentType::PumpConstantSpeed:
     959             :                             case DataPlant::PlantEquipmentType::PumpVariableSpeed:
     960             :                             case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
     961      409178 :                                 if (CompIndex > 0) {
     962             :                                     ThisBranchFlowRequestNeedIfOn =
     963      409178 :                                         max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
     964             :                                 }
     965      409178 :                                 break;
     966           0 :                             case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
     967           0 :                                 if (CompIndex > 0) {
     968           0 :                                     ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
     969           0 :                                                                         state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
     970           0 :                                                                             state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
     971             :                                 }
     972           0 :                                 break;
     973           0 :                             default:
     974           0 :                                 ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
     975           0 :                                 break;
     976             :                             }
     977             : 
     978    37019315 :                         } else if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
     979    35456988 :                                    (loop.CommonPipeType == DataPlant::CommonPipeType::Single)) {
     980      255612 :                             int const CompIndex = component.CompNum;
     981      255612 :                             switch (component.Type) {
     982             :                             // remove var speed pumps from this case statement if can set MassFlowRateRequest
     983      255612 :                             case DataPlant::PlantEquipmentType::PumpConstantSpeed:
     984             :                             case DataPlant::PlantEquipmentType::PumpVariableSpeed:
     985             :                             case DataPlant::PlantEquipmentType::PumpBankVariableSpeed: {
     986      255612 :                                 if (CompIndex > 0) {
     987             :                                     ThisBranchFlowRequestNeedIfOn =
     988      255612 :                                         max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
     989             :                                 }
     990      255612 :                                 break;
     991             :                             }
     992           0 :                             case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
     993           0 :                                 if (CompIndex > 0) {
     994           0 :                                     ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
     995           0 :                                                                         state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
     996           0 :                                                                             state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
     997             :                                 }
     998           0 :                                 break;
     999           0 :                             default:
    1000           0 :                                 ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
    1001             :                             }
    1002      255612 :                         } else {
    1003    36354525 :                             int const CompIndex = component.CompNum;
    1004    36354525 :                             switch (component.Type) {
    1005     6249696 :                             case DataPlant::PlantEquipmentType::PumpConstantSpeed:
    1006     6249696 :                                 if (CompIndex > 0) {
    1007     6249696 :                                     auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
    1008     6249696 :                                     if (ParallelBranchIndex >= 1) { // branch pump
    1009      277447 :                                         if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
    1010      129539 :                                             ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
    1011      147908 :                                         } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
    1012       18140 :                                             ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
    1013             :                                         }
    1014      277447 :                                         loop_side.hasConstSpeedBranchPumps = true;
    1015      277447 :                                         branch.HasConstantSpeedBranchPump = true;
    1016      277447 :                                         branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax;
    1017             :                                     } else { // inlet pump
    1018     5972249 :                                         ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
    1019             :                                     }
    1020             :                                 }
    1021     6249696 :                                 break;
    1022       14520 :                             case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
    1023       14520 :                                 if (CompIndex > 0) {
    1024       14520 :                                     auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
    1025       14520 :                                     if (ParallelBranchIndex >= 1) { // branch pump
    1026           0 :                                         if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
    1027             :                                             ThisBranchFlowRequestNeedIfOn =
    1028           0 :                                                 max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
    1029           0 :                                         } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
    1030             :                                             ThisBranchFlowRequestNeedIfOn =
    1031           0 :                                                 max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
    1032             :                                         }
    1033           0 :                                         loop_side.hasConstSpeedBranchPumps = true;
    1034           0 :                                         branch.HasConstantSpeedBranchPump = true;
    1035           0 :                                         branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax / this_pump.NumPumpsInBank;
    1036             :                                     } else { // inlet pump
    1037             :                                         ThisBranchFlowRequestNeedIfOn =
    1038       14520 :                                             max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
    1039             :                                     }
    1040             :                                 }
    1041       14520 :                                 break;
    1042             : 
    1043             :                                 // overwrite here for branch pumps
    1044    30090309 :                             case DataPlant::PlantEquipmentType::PumpVariableSpeed:
    1045             :                             case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
    1046             :                             case DataPlant::PlantEquipmentType::PumpCondensate:
    1047    30090309 :                                 if (component.CompNum > 0) {
    1048    30090309 :                                     auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
    1049    30090309 :                                     this_pump.LoopSolverOverwriteFlag = false;
    1050             :                                 }
    1051             :                             default:
    1052    30090309 :                                 break;
    1053             :                             }
    1054             :                         }
    1055             :                     }
    1056             :                 }
    1057   460787740 :                 if (BranchCounter == 1) { // inlet branch
    1058    72571882 :                     InletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
    1059    72571882 :                     InletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
    1060   388215858 :                 } else if (BranchCounter < NumBranchesOnThisLoopSide) { // branchcounter = 1 is already caught
    1061   315968464 :                     ParallelBranchRequestsNeedAndTurnOn += ThisBranchFlowRequestNeedAndTurnOn;
    1062   315968464 :                     ParallelBranchRequestsNeedIfOn += ThisBranchFlowRequestNeedIfOn;
    1063    72247394 :                 } else if (BranchCounter == NumBranchesOnThisLoopSide) { // outlet branch
    1064    72247394 :                     OutletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
    1065    72247394 :                     OutletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
    1066             :                 }
    1067             : 
    1068   460787740 :                 branch.RequestedMassFlow = max(ThisBranchFlowRequestNeedIfOn, ThisBranchFlowRequestNeedAndTurnOn);
    1069             :             }
    1070    72571882 :             loop_side.flowRequestNeedAndTurnOn =
    1071    72571882 :                 max(InletBranchRequestNeedAndTurnOn, ParallelBranchRequestsNeedAndTurnOn, OutletBranchRequestNeedAndTurnOn);
    1072    72571882 :             loop_side.flowRequestNeedIfOn = max(InletBranchRequestNeedIfOn, ParallelBranchRequestsNeedIfOn, OutletBranchRequestNeedIfOn);
    1073             :         }
    1074             : 
    1075    36285941 :         auto &this_loop_side(loop.LoopSide(this->plantLoc.loopSideNum));
    1076    36285941 :         auto &other_loop_side(loop.LoopSide(OtherSide));
    1077             : 
    1078             :         //~ Now that we have calculated each sides different status's requests, process to find final
    1079    36285941 :         if ((this_loop_side.flowRequestNeedAndTurnOn + other_loop_side.flowRequestNeedAndTurnOn) < DataBranchAirLoopPlant::MassFlowTolerance) {
    1080    16975360 :             this_loop_side.flowRequestFinal = 0.0;
    1081    16975360 :             other_loop_side.flowRequestFinal = 0.0;
    1082             :         } else { // some flow is needed and loop should try to run
    1083    19310581 :             this_loop_side.flowRequestFinal = max(this_loop_side.flowRequestNeedAndTurnOn, this_loop_side.flowRequestNeedIfOn);
    1084    19310581 :             other_loop_side.flowRequestFinal = max(other_loop_side.flowRequestNeedAndTurnOn, other_loop_side.flowRequestNeedIfOn);
    1085             :         }
    1086             :         // now store final flow requests on each loop side data structure
    1087    36285941 :         this_loop_side.FlowRequest = this_loop_side.flowRequestFinal;
    1088    36285941 :         other_loop_side.FlowRequest = other_loop_side.flowRequestFinal;
    1089             : 
    1090    36285941 :         if (loop.CommonPipeType == DataPlant::CommonPipeType::No) {
    1091             :             // we may or may not have a pump on this side, but the flow request is the larger of the two side's final
    1092    35588265 :             if ((!this_loop_side.hasConstSpeedBranchPumps) && (!other_loop_side.hasConstSpeedBranchPumps)) {
    1093    35461638 :                 LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
    1094             :             } else { // account for stepped loop flow rates required of branch pumps
    1095             : 
    1096             :                 // rules for setting flow when there are constant speed branch pumps.
    1097             :                 // 1. Check if above routines already selected a loop flow rate based on the constant speed branches, if so then just use it
    1098      126627 :                 if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal >= other_loop_side.flowRequestFinal)) {
    1099             :                     // okay, just use basic logic
    1100       67063 :                     LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
    1101       59564 :                 } else if (other_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal <= other_loop_side.flowRequestFinal)) {
    1102             :                     // okay, just use basic logic
    1103       13455 :                     LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
    1104             :                 } else { // not okay, we have a case that will likely need special correcting
    1105             :                     //  2. determine which loop side has the stepped data
    1106       46109 :                     DataPlant::LoopSideLocation LoopSideIndex = DataPlant::LoopSideLocation::Invalid;
    1107       46109 :                     if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal < other_loop_side.flowRequestFinal)) {
    1108       33485 :                         LoopSideIndex = this->plantLoc.loopSideNum;
    1109       12624 :                     } else if (other_loop_side.hasConstSpeedBranchPumps && (other_loop_side.flowRequestFinal < this_loop_side.flowRequestFinal)) {
    1110       12624 :                         LoopSideIndex = OtherSide;
    1111             :                     }
    1112       46109 :                     auto &loop_side(loop.LoopSide(LoopSideIndex));
    1113             : 
    1114             :                     // 3. step through and find out needed information
    1115             :                     // 3a.  search the loop side with branch pumps and find the steps available with non-zero Myloads
    1116             :                     // 3b.  search the loop side with branch pumps and find the steps available with zero Myloads
    1117             :                     //                    LoadedConstantSpeedBranchFlowRateSteps = 0.0;
    1118       46109 :                     Real64 LoadedConstantSpeedBranchFlowRateSteps_sum = 0.0;
    1119       46109 :                     this_loop_side.noLoadConstantSpeedBranchFlowRateSteps = 0.0;
    1120       46109 :                     Real64 NoLoadConstantSpeedBranchFlowRateSteps_sum = 0.0;
    1121       46109 :                     int ParallelBranchIndex = 0;
    1122       46109 :                     int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
    1123       46109 :                     auto const &loop_branches(loop_side.Branch);
    1124      276654 :                     for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
    1125      230545 :                         auto const &loop_branch(loop_branches(BranchCounter));
    1126      230545 :                         if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) ++ParallelBranchIndex;
    1127      230545 :                         if (loop_branch.HasConstantSpeedBranchPump) {
    1128       92218 :                             Real64 const branch_mass_flow(loop_branch.ConstantSpeedBranchMassFlow);
    1129       92218 :                             if (loop_branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
    1130       20132 :                                 LoadedConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
    1131             :                             } else {
    1132       72086 :                                 this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex) = branch_mass_flow;
    1133       72086 :                                 NoLoadConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
    1134             :                             }
    1135             :                         }
    1136             :                     }
    1137             : 
    1138             :                     // 4. allocate which branches to use,
    1139       46109 :                     Real64 tmpLoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
    1140       46109 :                     Real64 MaxBranchPumpLoopSideFlow = LoadedConstantSpeedBranchFlowRateSteps_sum + NoLoadConstantSpeedBranchFlowRateSteps_sum;
    1141       46109 :                     tmpLoopFlow = min(tmpLoopFlow, MaxBranchPumpLoopSideFlow);
    1142             :                     //  4b. first use all the branches with non-zero MyLoad
    1143       46109 :                     if (tmpLoopFlow > LoadedConstantSpeedBranchFlowRateSteps_sum) {
    1144       46109 :                         Real64 AccumFlowSteps = LoadedConstantSpeedBranchFlowRateSteps_sum;
    1145       46109 :                         ParallelBranchIndex = 0;
    1146      138327 :                         for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
    1147      138327 :                             if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) {
    1148       92218 :                                 ++ParallelBranchIndex;
    1149             :                             } else {
    1150       46109 :                                 continue;
    1151             :                             }
    1152       92218 :                             Real64 const steps = this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex);
    1153       92218 :                             if (steps > 0.0) { // add in branches with zero MyLoad  in branch input order until satisfied
    1154       72086 :                                 if (tmpLoopFlow > AccumFlowSteps) {
    1155       72086 :                                     if (tmpLoopFlow <= AccumFlowSteps + steps) { // found it set requests and exit
    1156       46109 :                                         tmpLoopFlow = AccumFlowSteps + steps;
    1157       46109 :                                         loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
    1158       46109 :                                         LoopFlow = tmpLoopFlow;
    1159       46109 :                                         break;
    1160             :                                     } else {
    1161       25977 :                                         AccumFlowSteps += steps;
    1162       25977 :                                         loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
    1163             :                                     }
    1164             :                                 }
    1165             :                             }
    1166             :                         }
    1167             :                     }
    1168             :                 }
    1169             :             }
    1170      697676 :         } else if (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
    1171      433748 :             LoopFlow = this_loop_side.flowRequestFinal;
    1172      263928 :         } else if (loop.CommonPipeType == DataPlant::CommonPipeType::Single) {
    1173      263928 :             LoopFlow = this_loop_side.flowRequestFinal;
    1174             :         }
    1175             : 
    1176             :         // overrides the loop solver flow request to allow loop pump to turn off when not in use
    1177    36285941 :         if (this_loop_side.TotalPumps == 1) {
    1178    18345984 :             if (LoopFlow < HVAC::VerySmallMassFlow) { // Update from dataconvergetols...
    1179    44288586 :                 for (int BranchCounter = 1; BranchCounter <= this_loop_side.TotalBranches; ++BranchCounter) {
    1180             :                     // reference
    1181    35891604 :                     auto &branch(this_loop_side.Branch(BranchCounter));
    1182    35891604 :                     int const NumCompsOnThisBranch = branch.TotalComponents;
    1183    72259835 :                     for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
    1184    36368231 :                         auto const &component(branch.Comp(CompCounter));
    1185    36368231 :                         switch (component.Type) {
    1186     7203455 :                         case DataPlant::PlantEquipmentType::PumpVariableSpeed:
    1187             :                         case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
    1188             :                         case DataPlant::PlantEquipmentType::PumpCondensate:
    1189     7203455 :                             if (component.CompNum > 0) {
    1190     7203455 :                                 auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
    1191     7203455 :                                 this_pump.LoopSolverOverwriteFlag = true;
    1192             :                             }
    1193             :                         default:
    1194    36368231 :                             break;
    1195             :                         }
    1196             :                     }
    1197             :                 }
    1198             :             }
    1199             :         }
    1200             : 
    1201    36285941 :         return LoopFlow;
    1202             :     }
    1203             : 
    1204    36285941 :     void HalfLoopData::DoFlowAndLoadSolutionPass(EnergyPlusData &state, LoopSideLocation OtherSide, int ThisSideInletNode, bool FirstHVACIteration)
    1205             :     {
    1206             : 
    1207             :         // This is passed in-out deep down into the depths where the load op manager calls EMS and EMS can shut down pumps
    1208    36285941 :         bool LoopShutDownFlag = false;
    1209             : 
    1210             :         // First thing is to setup mass flow request information
    1211    36285941 :         Real64 ThisLoopSideFlowRequest = this->SetupLoopFlowRequest(state, OtherSide);
    1212             : 
    1213             :         // Now we know what the loop would "like" to run at, let's see the pump
    1214             :         // operation range (min/max avail) to see whether it is possible this time around
    1215    36285941 :         Real64 ThisLoopSideFlow = this->DetermineLoopSideFlowRate(state, ThisSideInletNode, ThisLoopSideFlowRequest);
    1216             : 
    1217   266928092 :         for (auto &branch : this->Branch) {
    1218   230642151 :             branch.lastComponentSimulated = 0;
    1219             :         }
    1220             : 
    1221             :         // We also need to establish a baseline "other-side-based" loop demand based on this possible flow rate
    1222    36285941 :         this->InitialDemandToLoopSetPoint = this->CalcOtherSideDemand(state, ThisLoopSideFlow);
    1223    36285941 :         this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
    1224    36285941 :         this->LoadToLoopSetPointThatWasntMet = 0.0;
    1225             : 
    1226             :         // We now have a loop side flow request, along with inlet min/max avails.
    1227             :         // We can now make a first pass through the component simulation, requesting flow as necessary.
    1228             :         // Normal "supply side" components will set a mass flow rate on their outlet node to request flow,
    1229             :         // while "Demand side" components will set a a mass flow request on their inlet node to request flow.
    1230    36285941 :         this->FlowLock = DataPlant::FlowLock::Unlocked;
    1231    36285941 :         this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
    1232             : 
    1233             :         // discussion/comments about loop solver/flow resolver interaction
    1234             :         // At this point, the components have been simulated.  They should have either:
    1235             :         //  - logged a massflowrequest
    1236             :         //  - or logged a MassFlowRate
    1237             :         // We need to decide what the components are going to do on FlowLock=0.
    1238             :         // If we want all control here at the solver level, the components just need to
    1239             :         //  log their MassFlowRate on their outlet nodes, or some other mechanism.
    1240             :         // Then the loop solver can scan the branch and get the max, and this will be the requested
    1241             :         //  flow rate for the branch.
    1242             :         // The loop solver will then set this as the branch outlet mass flow rate in preparation
    1243             :         //  for the flow resolver.
    1244             :         // The loop solver may need to do something to the inlet/outlet branch, but I'm not sure yet.
    1245             :         // The following comment block is what I had already thought of, and it may still make sense.
    1246             : 
    1247             :         // Now that all the flow requests have been logged, we need to prepare them for the
    1248             :         //  flow resolver.  This will just take the requests and determine the desired flow
    1249             :         //  request for that branch according to pump placement, pump type, and other component
    1250             :         //  conditions.  In many cases, this will just be to simply take the max request from
    1251             :         //  the branch, which will already be within pumping limits for that flow path.
    1252             :         // We can then call the flow resolver to lock down branch inlet flow rates.
    1253             : 
    1254             :         // The flow resolver takes information such as requested flows and min/max available flows and
    1255             :         //  sets the corrected flow on the inlet to each parallel branch
    1256    36285941 :         this->ResolveParallelFlows(state, ThisLoopSideFlow, FirstHVACIteration);
    1257             : 
    1258             :         // Re-Initialize variables for this next pass
    1259    36285941 :         this->InitialDemandToLoopSetPointSAVED = this->InitialDemandToLoopSetPoint;
    1260    36285941 :         this->CurrentAlterationsToDemand = 0.0;
    1261    36285941 :         this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
    1262             : 
    1263             :         // Now that flow rates have been resolved, we just need to set the flow lock status
    1264             :         //  flag, and resimulate.  During this simulation each component will still use the
    1265             :         //  SetFlowRequest routine, but this routine will also set the outlet flow rate
    1266             :         //  equal to the inlet flow rate, according to flowlock logic.
    1267    36285941 :         this->FlowLock = DataPlant::FlowLock::Locked;
    1268    36285941 :         this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
    1269    36285941 :     }
    1270             : 
    1271    36285941 :     void HalfLoopData::ResolveParallelFlows(EnergyPlusData &state,
    1272             :                                             Real64 const ThisLoopSideFlow, // [kg/s]  total flow to be split
    1273             :                                             bool const FirstHVACIteration  // TRUE if First HVAC iteration of Time step
    1274             :     )
    1275             :     {
    1276             : 
    1277             :         // SUBROUTINE INFORMATION:
    1278             :         //       AUTHOR         Brandon Anderson, Dan Fisher
    1279             :         //       DATE WRITTEN   October 1999
    1280             :         //       MODIFIED       May 2005 Sankaranarayanan K P, Rich Liesen
    1281             :         //       RE-ENGINEERED  Sept 2010 Dan Fisher, Brent Griffith for demand side update
    1282             : 
    1283             :         // PURPOSE OF THIS SUBROUTINE:
    1284             :         // This subroutine takes the overall loop side flow and distributes
    1285             :         // it among parallel branches. this is the main implementation of
    1286             :         // flow splitting for plant splitter/mixer
    1287             : 
    1288             :         // METHODOLOGY EMPLOYED:
    1289             :         // Flow through the branches is currently determined by
    1290             :         // the active component on the branch, as well as the
    1291             :         // order of the branches following the splitter.
    1292             :         // SimPlantEquipment is run first, and the active components
    1293             :         // request their flow.  These flows are compared and a simple
    1294             :         // algorithm balances flow in the branches.  The flow in these
    1295             :         // branches is then locked down, via MassFlowRateMaxAvail and MinAvail
    1296             :         // SimPlant Equipment is then run again in order to get correct
    1297             :         // properties.  Finally, Max/MinAvail are reset for the next time step.
    1298             : 
    1299             :         // Using/Aliasing
    1300             : 
    1301             :         // SUBROUTINE PARAMETER DEFINITIONS:
    1302    36285941 :         int constexpr LoopSideSingleBranch(1); // For readability
    1303             : 
    1304             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1305             :         Real64 ActiveFlowRate;        // The flow available when cycling through branches
    1306             :         Real64 PassiveFlowRate;       // The flow available when cycling through branches
    1307             :         Real64 FracFlow;              // The flow available when cycling through branches
    1308             :         Real64 ThisBranchRequestFrac; // The request ratio
    1309             :         Real64 totalMax;              // The flow available when cycling through branches
    1310             :         Real64 FlowRemaining;         // The flow available when cycling through branches
    1311             :         int LastNodeOnBranch;         // intermediate value used for better readabilty
    1312             :         int FirstNodeOnBranch;        // intermediate value used for better readabilty
    1313             :         Real64 BranchFlowReq;
    1314             :         Real64 BranchMinAvail;
    1315             :         Real64 BranchMaxAvail;
    1316             :         Real64 ParallelBranchMaxAvail;
    1317             :         Real64 ParallelBranchMinAvail;
    1318             :         Real64 TotParallelBranchFlowReq;
    1319             :         Real64 StartingFlowRate;
    1320             :         Real64 ThisBranchRequest;
    1321             : 
    1322             :         // If there is no splitter then there is no continuity to enforce.
    1323    36285941 :         if (!this->Splitter.Exists) {
    1324             : 
    1325             :             // If there's only one branch, then RETURN
    1326      160858 :             if (this->TotalBranches == 1) {
    1327             :                 // The branch should just try to meet the request previously calculated.  This should be good,
    1328             :                 // just need to make sure that during FlowUnlocked, no one constrained Min/Max farther.
    1329             :                 // This would have been propagated down the branch, so we can check the outlet node min/max avail for this.
    1330      160858 :                 auto const &this_single_branch(this->Branch(LoopSideSingleBranch));
    1331      160858 :                 LastNodeOnBranch = this_single_branch.NodeNumOut;
    1332      160858 :                 FirstNodeOnBranch = this_single_branch.NodeNumIn;
    1333      160858 :                 BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
    1334      160858 :                 BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
    1335      160858 :                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = min(max(ThisLoopSideFlow, BranchMinAvail), BranchMaxAvail);
    1336             :                 // now with flow locked, this single branch will just ran at the specified flow rate, so we are done
    1337    31763770 :                 return;
    1338             :             } else {
    1339           0 :                 ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
    1340           0 :                 ShowContinueError(state, "There are multiple branches, yet no splitter.  This is an invalid configuration.");
    1341           0 :                 ShowContinueError(state, "Add a set of connectors, use put components on a single branch.");
    1342           0 :                 ShowFatalError(state, "Invalid plant topology causes program termination.");
    1343           0 :                 return;
    1344             :             }
    1345             :         }
    1346             : 
    1347             :         // If a splitter/mixer combination exist on the loop
    1348    36125083 :         if (this->Splitter.Exists && this->Mixer.Exists) {
    1349             : 
    1350             :             // Zero out local variables
    1351    36125083 :             TotParallelBranchFlowReq = 0.0;
    1352    36125083 :             int NumSplitOutlets = this->Splitter.TotalOutletNodes;
    1353    36125083 :             if (NumSplitOutlets < 1) {
    1354           0 :                 ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
    1355           0 :                 ShowContinueError(state, "Diagnostic error in PlantLoopSolver::ResolveParallelFlows.");
    1356           0 :                 ShowContinueError(state, "Splitter improperly specified, no splitter outlets.");
    1357           0 :                 ShowFatalError(state, "Invalid plant topology causes program termination.");
    1358             :             }
    1359             : 
    1360    36125083 :             int NumActiveBranches = 0;
    1361    36125083 :             ParallelBranchMaxAvail = 0.0;
    1362    36125083 :             ParallelBranchMinAvail = 0.0;
    1363   194356210 :             for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
    1364             : 
    1365   158231127 :                 int BranchNum = this->Splitter.BranchNumOut(iBranch);
    1366   158231127 :                 auto &this_branch(this->Branch(BranchNum));
    1367   158231127 :                 int SplitterBranchOut = this->Splitter.BranchNumOut(iBranch);
    1368   158231127 :                 auto const &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
    1369   158231127 :                 LastNodeOnBranch = this_branch.NodeNumOut;
    1370   158231127 :                 FirstNodeOnBranch = this_branch.NodeNumIn;
    1371   158231127 :                 BranchFlowReq = this_branch.DetermineBranchFlowRequest(state);
    1372   158231127 :                 this_branch.RequestedMassFlow = BranchFlowReq; // store this for later use in logic for remaining flow allocations
    1373             :                 // now, if we are have branch pumps, here is the situation:
    1374             :                 // constant speed pumps lock in a flow request on the inlet node
    1375             :                 // variable speed pumps which have other components on the branch do not log a request themselves
    1376             :                 // the DetermineBranchFlowRequest routine only looks at the branch inlet node
    1377             :                 // for variable speed branch pumps then, this won't work because the branch will be requesting zero
    1378             :                 // so let's adjust for this here to make sure these branches get good representation
    1379             :                 // This comment above is not true, for series active branches, DetermineBranchFlowRequest does scan down the branch's
    1380             :                 // components already, no need to loop over components
    1381   158231127 :                 BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
    1382   158231127 :                 BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
    1383             :                 //            !sum the branch flow requests to a total parallel branch flow request
    1384   158231127 :                 bool activeBranch = this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active;
    1385   158231127 :                 bool isSeriesActiveAndRequesting =
    1386   158231127 :                     (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) && (BranchFlowReq > 0.0);
    1387   158231127 :                 if (activeBranch || isSeriesActiveAndRequesting) { // revised logic for series active
    1388   122730579 :                     TotParallelBranchFlowReq += BranchFlowReq;
    1389   122730579 :                     ++NumActiveBranches;
    1390             :                 }
    1391   158231127 :                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = BranchFlowReq;
    1392   158231127 :                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail = BranchMinAvail;
    1393   158231127 :                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail = BranchMaxAvail;
    1394   158231127 :                 ParallelBranchMaxAvail += BranchMaxAvail;
    1395   158231127 :                 ParallelBranchMinAvail += BranchMinAvail;
    1396             :             }
    1397             :             //            ! Find branch number and flow rates at splitter inlet
    1398    36125083 :             int SplitterBranchIn = this->Splitter.BranchNumIn;
    1399    36125083 :             int FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
    1400             :             //            ! Find branch number and flow rates at mixer outlet
    1401    36125083 :             int MixerBranchOut = this->Mixer.BranchNumOut;
    1402    36125083 :             LastNodeOnBranch = this->Branch(MixerBranchOut).NodeNumOut;
    1403    36125083 :             int FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
    1404             : 
    1405    36125083 :             auto &first_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchIn));
    1406    36125083 :             auto &last_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchOut));
    1407             : 
    1408             :             // Reset branch inlet node flow rates for the first and last branch on loop
    1409    36125083 :             first_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
    1410    36125083 :             last_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
    1411             : 
    1412             :             // Reset branch inlet node Min/MaxAvails for the first and last branch on loop
    1413    36125083 :             first_branch_inlet_node.MassFlowRateMaxAvail = min(first_branch_inlet_node.MassFlowRateMaxAvail, ParallelBranchMaxAvail);
    1414    36125083 :             first_branch_inlet_node.MassFlowRateMaxAvail =
    1415    36125083 :                 min(first_branch_inlet_node.MassFlowRateMaxAvail, last_branch_inlet_node.MassFlowRateMaxAvail);
    1416    36125083 :             first_branch_inlet_node.MassFlowRateMinAvail = max(first_branch_inlet_node.MassFlowRateMinAvail, ParallelBranchMinAvail);
    1417    36125083 :             first_branch_inlet_node.MassFlowRateMinAvail =
    1418    36125083 :                 max(first_branch_inlet_node.MassFlowRateMinAvail, last_branch_inlet_node.MassFlowRateMinAvail);
    1419    36125083 :             last_branch_inlet_node.MassFlowRateMinAvail = first_branch_inlet_node.MassFlowRateMinAvail;
    1420    36125083 :             last_branch_inlet_node.MassFlowRateMaxAvail = first_branch_inlet_node.MassFlowRateMaxAvail;
    1421             : 
    1422             :             // Initialize the remaining flow variable
    1423    36125083 :             FlowRemaining = ThisLoopSideFlow;
    1424             : 
    1425             :             // Initialize flow on passive, bypass and uncontrolled parallel branches to zero.  For these branches
    1426             :             // MinAvail is not enforced
    1427   194356210 :             for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1428   158231127 :                 int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1429   158231127 :                 FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1430   194126548 :                 if (this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::Active &&
    1431    35895421 :                     this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::SeriesActive) {
    1432    34977352 :                     state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
    1433    69954704 :                     this->PushBranchFlowCharacteristics(
    1434    34977352 :                         state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1435             :                 }
    1436             :             }
    1437             : 
    1438             :             // IF SUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
    1439    36125083 :             if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) { // no flow available at all for splitter
    1440    86735032 :                 for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1441    69815470 :                     int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1442   140043143 :                     for (int CompCounter = 1; CompCounter <= this->Branch(SplitterBranchOut).TotalComponents; ++CompCounter) {
    1443             : 
    1444    70227673 :                         FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1445    70227673 :                         int CompInletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumIn;
    1446    70227673 :                         int CompOutletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumOut;
    1447    70227673 :                         state.dataLoopNodes->Node(CompInletNode).MassFlowRate = 0.0;
    1448    70227673 :                         state.dataLoopNodes->Node(CompInletNode).MassFlowRateMaxAvail = 0.0;
    1449    70227673 :                         state.dataLoopNodes->Node(CompOutletNode).MassFlowRate = 0.0;
    1450    70227673 :                         state.dataLoopNodes->Node(CompOutletNode).MassFlowRateMaxAvail = 0.0;
    1451             :                     }
    1452             :                 }
    1453    16919562 :                 return;
    1454    19205521 :             } else if (FlowRemaining >= TotParallelBranchFlowReq) {
    1455             : 
    1456             :                 // 1) Satisfy flow demand of ACTIVE splitter outlet branches
    1457    84895042 :                 for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1458    70211692 :                     int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1459    70211692 :                     FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1460    84808120 :                     if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
    1461    14596428 :                         this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
    1462             :                         // branch flow is min of requested flow and remaining flow
    1463    56012802 :                         state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
    1464    56012802 :                             min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FlowRemaining);
    1465    56012802 :                         if (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance)
    1466    12477042 :                             state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
    1467   112025604 :                         this->PushBranchFlowCharacteristics(
    1468    56012802 :                             state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1469    56012802 :                         FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1470    56012802 :                         if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) FlowRemaining = 0.0;
    1471             :                     }
    1472             :                 }
    1473             :                 // IF the active branches take the entire loop flow, return
    1474    14683350 :                 if (FlowRemaining == 0.0) return;
    1475             : 
    1476             :                 // 2) Distribute remaining flow to PASSIVE branches
    1477     4212550 :                 totalMax = 0.0;
    1478    26955151 :                 for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1479    22742601 :                     int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1480    22742601 :                     FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1481    22742601 :                     if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
    1482             :                         // Calculate the total max available
    1483           0 :                         totalMax += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
    1484             :                     }
    1485             :                 }
    1486             : 
    1487     4212550 :                 if (totalMax > 0) {
    1488           0 :                     for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1489           0 :                         int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1490           0 :                         FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1491           0 :                         if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
    1492           0 :                             FracFlow = FlowRemaining / totalMax;
    1493           0 :                             if (FracFlow <= 1.0) { // the passive branches will take all the flow
    1494           0 :                                 PassiveFlowRate = FracFlow * state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
    1495             :                                 // Check against FlowRemaining
    1496           0 :                                 PassiveFlowRate = min(FlowRemaining, PassiveFlowRate);
    1497             :                                 // Allow FlowRequest to be increased to meet minimum on branch
    1498           0 :                                 PassiveFlowRate = max(PassiveFlowRate, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail);
    1499           0 :                                 FlowRemaining = max((FlowRemaining - PassiveFlowRate), 0.0);
    1500           0 :                                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = PassiveFlowRate;
    1501             :                             } else { // Each Branch receives maximum flow and BYPASS must be used
    1502           0 :                                 state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
    1503           0 :                                     min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail, FlowRemaining);
    1504           0 :                                 FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1505             :                             }
    1506           0 :                             this->PushBranchFlowCharacteristics(
    1507           0 :                                 state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1508             :                         }
    1509             :                     }
    1510             :                 } // totalMax <=0 and flow should be assigned to active branches
    1511             :                 // IF the passive branches take the remaining loop flow, return
    1512     4212550 :                 if (FlowRemaining == 0.0) return;
    1513             : 
    1514             :                 // 3) Distribute remaining flow to the BYPASS
    1515    26955151 :                 for (int OutletNum = 1; OutletNum <= this->Splitter.TotalOutletNodes; ++OutletNum) {
    1516    22742601 :                     int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1517    22742601 :                     FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1518    22742601 :                     if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Bypass) {
    1519     3975987 :                         state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
    1520     3975987 :                             min(FlowRemaining, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
    1521     7951974 :                         this->PushBranchFlowCharacteristics(
    1522     3975987 :                             state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1523     3975987 :                         FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1524             :                     }
    1525             :                 }
    1526             :                 // IF the bypass take the remaining loop flow, return
    1527     4212550 :                 if (FlowRemaining == 0.0) return;
    1528             : 
    1529             :                 // 4) If PASSIVE branches and BYPASS are at max and there's still flow, distribute remaining flow to ACTIVE branches but only those
    1530             :                 // that had a non-zero flow request. Try to leave branches off that wanted to be off.
    1531      243793 :                 if (NumActiveBranches > 0) {
    1532      196191 :                     ActiveFlowRate = FlowRemaining / NumActiveBranches; // denominator now only includes active branches that wanted to be "on"
    1533      279720 :                     for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1534      262769 :                         int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1535      262769 :                         FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1536      262769 :                         bool branchIsActive = this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active;
    1537             :                         bool branchIsSeriesActiveAndRequesting =
    1538      280966 :                             this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive &&
    1539       18197 :                             this->Branch(SplitterBranchOut).RequestedMassFlow > 0.0;
    1540      262769 :                         if (branchIsActive || branchIsSeriesActiveAndRequesting) { // only series active branches that want to be "on"
    1541             :                             // check Remaining flow (should be correct!)
    1542      260967 :                             ActiveFlowRate = min(ActiveFlowRate, FlowRemaining);
    1543             :                             // set the flow rate to the MIN((MassFlowRate+AvtiveFlowRate), MaxAvail)
    1544      260967 :                             StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1545      260967 :                             state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
    1546      260967 :                                 min((state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate + ActiveFlowRate),
    1547      260967 :                                     state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
    1548      521934 :                             this->PushBranchFlowCharacteristics(
    1549      260967 :                                 state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1550             :                             // adjust the remaining flow
    1551      260967 :                             FlowRemaining -= (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate - StartingFlowRate);
    1552             :                         }
    1553      262769 :                         if (FlowRemaining == 0) break;
    1554             :                     }
    1555             :                     // IF the active branches take the remaining loop flow, return
    1556      196191 :                     if (FlowRemaining == 0.0) return;
    1557             : 
    1558             :                     // 5)  Step 4) could have left ACTIVE branches < MaxAvail.  Check to makes sure all ACTIVE branches are at MaxAvail
    1559       56839 :                     for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1560       39888 :                         int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1561       39888 :                         FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1562       44260 :                         if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
    1563        4372 :                             this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
    1564       38988 :                             StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1565             :                             ActiveFlowRate =
    1566       38988 :                                 min(FlowRemaining, (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail - StartingFlowRate));
    1567       38988 :                             FlowRemaining -= ActiveFlowRate;
    1568       38988 :                             state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = StartingFlowRate + ActiveFlowRate;
    1569       77976 :                             this->PushBranchFlowCharacteristics(
    1570       38988 :                                 state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1571             :                         }
    1572             :                     }
    1573             :                 }
    1574             :                 // IF the active branches take the remaining loop flow, return
    1575       64553 :                 if (FlowRemaining == 0.0) return;
    1576             : 
    1577             :                 // 6) Adjust Inlet branch and outlet branch flow rates to match parallel branch rate
    1578       56600 :                 TotParallelBranchFlowReq = 0.0;
    1579      120404 :                 for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
    1580       63804 :                     int BranchNum = this->Splitter.BranchNumOut(iBranch);
    1581       63804 :                     FirstNodeOnBranch = this->Branch(BranchNum).NodeNumIn;
    1582             :                     // calculate parallel branch flow rate
    1583       63804 :                     TotParallelBranchFlowReq += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1584             :                 }
    1585             :                 // Reset the flow on the splitter inlet branch
    1586       56600 :                 SplitterBranchIn = this->Splitter.BranchNumIn;
    1587       56600 :                 FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
    1588       56600 :                 state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate = TotParallelBranchFlowReq;
    1589      113200 :                 this->PushBranchFlowCharacteristics(
    1590       56600 :                     state, SplitterBranchIn, state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate, FirstHVACIteration);
    1591             :                 // Reset the flow on the Mixer outlet branch
    1592       56600 :                 MixerBranchOut = this->Mixer.BranchNumOut;
    1593       56600 :                 FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
    1594       56600 :                 state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
    1595      113200 :                 this->PushBranchFlowCharacteristics(
    1596       56600 :                     state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
    1597       56600 :                 return;
    1598             : 
    1599             :                 // IF INSUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
    1600             :             } else {
    1601             : 
    1602             :                 // 1) apportion flow based on requested fraction of total
    1603    22726136 :                 for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
    1604             : 
    1605    18203965 :                     int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
    1606    18203965 :                     ThisBranchRequest = this->Branch(SplitterBranchOut).DetermineBranchFlowRequest(state);
    1607    18203965 :                     FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
    1608    18203965 :                     auto &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
    1609             : 
    1610    18203965 :                     if ((this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active) ||
    1611     4374288 :                         (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive)) {
    1612             : 
    1613             :                         // since we are calculating this fraction based on the total parallel request calculated above, we must mimic the logic to
    1614             :                         // make sure the math works every time that means we must make the variable speed pump correction here as well.
    1615    28093322 :                         for (int CompCounter = 1; CompCounter <= this_splitter_outlet_branch.TotalComponents; ++CompCounter) {
    1616             : 
    1617    14118989 :                             auto const &this_comp(this_splitter_outlet_branch.Comp(CompCounter));
    1618             : 
    1619             :                             // if this isn't a variable speed pump then just keep cycling
    1620    14118989 :                             if ((this_comp.Type != PlantEquipmentType::PumpVariableSpeed) &&
    1621    14117347 :                                 (this_comp.Type != PlantEquipmentType::PumpBankVariableSpeed)) {
    1622    14117347 :                                 continue;
    1623             :                             }
    1624             : 
    1625        1642 :                             int CompInletNode = this_comp.NodeNumIn;
    1626        1642 :                             ThisBranchRequest = max(ThisBranchRequest, state.dataLoopNodes->Node(CompInletNode).MassFlowRateRequest);
    1627             :                         }
    1628             : 
    1629    13974333 :                         ThisBranchRequestFrac = ThisBranchRequest / TotParallelBranchFlowReq;
    1630             :                         //    FracFlow = state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate/TotParallelBranchFlowReq
    1631             :                         //    state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate = MIN((FracFlow *
    1632             :                         //    state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate),FlowRemaining)
    1633    13974333 :                         state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = ThisBranchRequestFrac * ThisLoopSideFlow;
    1634    27948666 :                         this->PushBranchFlowCharacteristics(
    1635    13974333 :                             state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
    1636    13974333 :                         FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
    1637             :                     }
    1638             :                 }
    1639             : 
    1640             :                 // 1b) check if flow all apportioned
    1641     4522171 :                 if (FlowRemaining > DataBranchAirLoopPlant::MassFlowTolerance) {
    1642             :                     // Call fatal diagnostic error. !The math should work out!
    1643           0 :                     ShowSevereError(state, "ResolveParallelFlows: Dev note, failed to redistribute restricted flow");
    1644           0 :                     ShowContinueErrorTimeStamp(state, "");
    1645           0 :                     ShowContinueError(state, format("Loop side flow = {:.8R} (kg/s)", ThisLoopSideFlow));
    1646           0 :                     ShowContinueError(state, format("Flow Remaining = {:.8R} (kg/s)", FlowRemaining));
    1647           0 :                     ShowContinueError(state, format("Parallel Branch requests  = {:.8R} (kg/s)", TotParallelBranchFlowReq));
    1648             :                 }
    1649             : 
    1650             :                 // 2)  ! Reset the flow on the Mixer outlet branch
    1651     4522171 :                 MixerBranchOut = this->Mixer.BranchNumOut;
    1652     4522171 :                 FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
    1653     4522171 :                 state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
    1654     9044342 :                 this->PushBranchFlowCharacteristics(
    1655     4522171 :                     state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
    1656             : 
    1657             :             } // Total flow requested >= or < Total parallel request
    1658             : 
    1659             :         } // Splitter/Mixer exists
    1660             :     }
    1661             : 
    1662   217072214 :     void HalfLoopData::SimulateLoopSideBranchGroup(EnergyPlusData &state,
    1663             :                                                    int const FirstBranchNum,
    1664             :                                                    int const LastBranchNum,
    1665             :                                                    Real64 FlowRequest,
    1666             :                                                    bool const FirstHVACIteration,
    1667             :                                                    bool &LoopShutDownFlag)
    1668             :     {
    1669             : 
    1670             :         // SUBROUTINE INFORMATION:
    1671             :         //       AUTHOR         Edwin Lee
    1672             :         //       DATE WRITTEN   July 2010
    1673             :         //       MODIFIED       na
    1674             :         //       RE-ENGINEERED  na
    1675             : 
    1676             :         // PURPOSE OF THIS SUBROUTINE:
    1677             :         // This routine will manage the component simulation on a single set of parallel branches
    1678             :         // This routine also reverts to a single branch simulation if there isn't a set of parallel branches
    1679             : 
    1680             :         // METHODOLOGY EMPLOYED:
    1681             :         // Loop through all components, and simulate first the non-load range based on each branch.
    1682             :         // When a load-range based (LRB) is encountered, the simulation moves to the next branch to do non-LRB components.
    1683             :         // When all paths are exhausted the simulation begins simulating LRB components.  Before each comp, the load distribution
    1684             :         //  engine is called to handle the load distribution for this current pass.  If load is successfully distributed, this is
    1685             :         //  flagged, and not called again.  If load is not distributed (i.e. this component isn't ON right now), then the
    1686             :         //  load distribution engine will be called again before the next component.
    1687             :         // After all load distribution is done and those components are complete, the simulation moves back to do any
    1688             :         //  remaining components that may be downstream.
    1689             : 
    1690             :         //~ Flags
    1691             :         bool LoadDistributionWasPerformed;
    1692             : 
    1693             :         //~ General variables
    1694             :         Real64 LoadToLoopSetPoint;
    1695   217072214 :         PlantLocation PumpLocation;
    1696   217072214 :         LoadToLoopSetPoint = 0.0;
    1697             : 
    1698             :         // We now know what plant simulation region is available to us, let's simulate this group
    1699   217072214 :         bool EncounteredLRBObjDuringPass1(false);
    1700   678356516 :         for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
    1701   461284302 :             auto &branch(this->Branch(BranchCounter));
    1702             : 
    1703             :             //~ Always start from the last component we did the last time around + 1 and
    1704             :             //~  try to make it all the way to the end of the loop
    1705   461284302 :             int const StartingComponent = branch.lastComponentSimulated + 1;
    1706   461284302 :             int const EndingComponent = branch.TotalComponents;
    1707   886456706 :             for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
    1708             : 
    1709   463821104 :                 auto &this_comp(branch.Comp(CompCounter));
    1710   463821104 :                 PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
    1711   463821104 :                 DataPlant::OpScheme const CurOpSchemeType(this_comp.CurOpSchemeType);
    1712             : 
    1713   463821104 :                 switch (CurOpSchemeType) {
    1714           0 :                 case DataPlant::OpScheme::WSEcon: //~ coils
    1715           0 :                     this_comp.MyLoad = UpdatedDemandToLoopSetPoint;
    1716           0 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1717           0 :                     break;
    1718    37404504 :                 case DataPlant::OpScheme::Pump: //~ pump
    1719    37404504 :                     if (this->BranchPumpsExist) {
    1720      712536 :                         SimulateSinglePump(state, this_comp.location, branch.RequestedMassFlow);
    1721             :                     } else {
    1722    36691968 :                         SimulateSinglePump(state, this_comp.location, FlowRequest);
    1723             :                     }
    1724    37404504 :                     break;
    1725     1160704 :                 case DataPlant::OpScheme::CompSetPtBased:
    1726     1160704 :                     PlantCondLoopOperation::ManagePlantLoadDistribution(state,
    1727             :                                                                         this_plantLoc,
    1728             :                                                                         LoadToLoopSetPoint,
    1729     1160704 :                                                                         LoadToLoopSetPointThatWasntMet,
    1730             :                                                                         FirstHVACIteration,
    1731             :                                                                         LoopShutDownFlag,
    1732             :                                                                         LoadDistributionWasPerformed);
    1733     1160704 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1734     1160704 :                     break;
    1735      322816 :                 case DataPlant::OpScheme::EMS:
    1736      322816 :                     if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
    1737      322816 :                         int const curCompOpSchemePtr = this_comp.CurCompLevelOpNum;
    1738      322816 :                         int const OpSchemePtr = this_comp.OpScheme(curCompOpSchemePtr).OpSchemePtr;
    1739      322816 :                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).OpScheme(OpSchemePtr).EMSIntVarLoopDemandRate = InitialDemandToLoopSetPoint;
    1740             :                     }
    1741      322816 :                     PlantCondLoopOperation::ManagePlantLoadDistribution(state,
    1742             :                                                                         this_plantLoc,
    1743      322816 :                                                                         UpdatedDemandToLoopSetPoint,
    1744      322816 :                                                                         LoadToLoopSetPointThatWasntMet,
    1745             :                                                                         FirstHVACIteration,
    1746             :                                                                         LoopShutDownFlag,
    1747             :                                                                         LoadDistributionWasPerformed);
    1748      322816 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1749      322816 :                     break;
    1750    38648700 :                 case OpScheme::WetBulbRB:
    1751             :                 case OpScheme::DryBulbRB:
    1752             :                 case OpScheme::DewPointRB:
    1753             :                 case OpScheme::RelHumRB:
    1754             :                 case OpScheme::DryBulbTDB:
    1755             :                 case OpScheme::WetBulbTDB:
    1756             :                 case OpScheme::DewPointTDB:
    1757             :                 case OpScheme::HeatingRB:
    1758             :                 case OpScheme::CoolingRB: { //~ load range based
    1759    38648700 :                     EncounteredLRBObjDuringPass1 = true;
    1760    38648700 :                     goto components_end; // don't do any more components on this branch
    1761             :                 }
    1762   386284380 :                 default: // demand, etc.
    1763   386284380 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1764             :                 }
    1765             : 
    1766             :                 // Update loop demand as needed for changes this component may have made
    1767   425172404 :                 this->UpdateAnyLoopDemandAlterations(state, BranchCounter, CompCounter);
    1768             : 
    1769             :                 //~ If we didn't EXIT early, we must have simulated, so update array
    1770   425172404 :                 branch.lastComponentSimulated = CompCounter;
    1771             : 
    1772             :             } //~ CompCounter
    1773   422635602 :         components_end:;
    1774             : 
    1775   461284302 :             if (this->FlowLock == DataPlant::FlowLock::Locked) {
    1776   230642151 :                 PlantPressureSystem::SimPressureDropSystem(
    1777             :                     state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
    1778             :             }
    1779             : 
    1780             :         } //~ BranchCounter
    1781             : 
    1782             :         // So now we have made one pass through all of the available components on these branches, skipping load based
    1783             :         // If we didn't encounter any load based objects during the first pass, then we must be done!
    1784   251052652 :         if (!EncounteredLRBObjDuringPass1) return;
    1785             : 
    1786             :         // If we have load based now, we should go ahead and distribute the load
    1787             :         // If not then this branch group is done, since flow path validation was previously done
    1788    33980438 :         LoadToLoopSetPoint = UpdatedDemandToLoopSetPoint;
    1789    33980438 :         LoadDistributionWasPerformed = false;
    1790             : 
    1791             :         // The way the load distribution is set up, I think I should call this for every load range based component
    1792             :         //  encountered until distribution is actually performed.  If we don't call for each component then we may
    1793             :         //  call for a component that is not on the current equip list and then nothing would come on.
    1794    33980438 :         bool EncounteredNonLBObjDuringPass2(false);
    1795   108341366 :         for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
    1796    74360928 :             auto &branch(this->Branch(BranchCounter));
    1797             : 
    1798             :             //~ Always start from the last component we did the last time around + 1 and
    1799             :             //~  try to make it all the way to the end of the loop
    1800    74360928 :             int const StartingComponent = branch.lastComponentSimulated + 1;
    1801    74360928 :             int const EndingComponent = branch.TotalComponents;
    1802   113775648 :             for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
    1803    39414720 :                 PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
    1804             : 
    1805    39414720 :                 DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
    1806             : 
    1807    39414720 :                 switch (CurOpSchemeType) {
    1808      129108 :                 case DataPlant::OpScheme::NoControl: //~ pipes, for example
    1809      129108 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1810      129108 :                     break;
    1811           0 :                 case DataPlant::OpScheme::Demand:
    1812             :                 case DataPlant::OpScheme::CompSetPtBased:
    1813             :                 case DataPlant::OpScheme::FreeRejection: //~ other control types
    1814           0 :                     EncounteredNonLBObjDuringPass2 = true;
    1815           0 :                     goto components2_end;       // don't do anymore components on this branch
    1816           0 :                 case DataPlant::OpScheme::Pump: //~ pump
    1817           0 :                     PumpLocation.loopNum = this->plantLoc.loopNum;
    1818           0 :                     PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
    1819           0 :                     PumpLocation.branchNum = BranchCounter;
    1820           0 :                     PumpLocation.compNum = CompCounter;
    1821           0 :                     if (this->BranchPumpsExist) {
    1822           0 :                         SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
    1823             :                     } else {
    1824           0 :                         SimulateSinglePump(state, PumpLocation, FlowRequest);
    1825             :                     }
    1826           0 :                     break;
    1827    39285612 :                 case OpScheme::WetBulbRB:
    1828             :                 case OpScheme::DryBulbRB:
    1829             :                 case OpScheme::DewPointRB:
    1830             :                 case OpScheme::RelHumRB:
    1831             :                 case OpScheme::DryBulbTDB:
    1832             :                 case OpScheme::WetBulbTDB:
    1833             :                 case OpScheme::DewPointTDB:
    1834             :                 case OpScheme::HeatingRB:
    1835             :                 case OpScheme::CoolingRB: {              //~ load range based
    1836    39285612 :                     if (!LoadDistributionWasPerformed) { //~ Still need to distribute load among load range based components
    1837    37227184 :                         PlantCondLoopOperation::ManagePlantLoadDistribution(state,
    1838             :                                                                             this_plantLoc,
    1839             :                                                                             LoadToLoopSetPoint,
    1840    37227184 :                                                                             LoadToLoopSetPointThatWasntMet,
    1841             :                                                                             FirstHVACIteration,
    1842             :                                                                             LoopShutDownFlag,
    1843             :                                                                             LoadDistributionWasPerformed);
    1844             :                     }
    1845    39285612 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1846    39285612 :                     break;
    1847             :                 }
    1848           0 :                 default:
    1849           0 :                     break;
    1850             :                 }
    1851             : 
    1852             :                 //~ If we didn't EXIT early, we must have simulated, so update array
    1853    39414720 :                 branch.lastComponentSimulated = CompCounter;
    1854             : 
    1855             :             } //~ CompCounter
    1856    74360928 :         components2_end:;
    1857             : 
    1858             :             //~ If we are locked, go ahead and simulate the pressure components on this branch
    1859    74360928 :             if (this->FlowLock == DataPlant::FlowLock::Locked) {
    1860    37180464 :                 PlantPressureSystem::SimPressureDropSystem(
    1861             :                     state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
    1862             :             }
    1863             : 
    1864             :         } //~ BranchCounter
    1865             : 
    1866             :         // So now we have made the load range based pass through all the components on each branch
    1867             :         // If we didn't see any other component types, then we are done, go away
    1868    33980438 :         if (!EncounteredNonLBObjDuringPass2) return;
    1869             : 
    1870             :         // If we did encounter other objects than we just need to go back through and simulate them
    1871           0 :         for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
    1872           0 :             auto &branch(this->Branch(BranchCounter));
    1873             : 
    1874             :             //~ Always start from the last component we did the last time around + 1 and
    1875             :             //~  try to make it all the way to the end of the loop
    1876           0 :             int const StartingComponent = branch.lastComponentSimulated + 1;
    1877           0 :             int const EndingComponent = branch.TotalComponents;
    1878           0 :             for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
    1879             : 
    1880           0 :                 DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
    1881             : 
    1882           0 :                 switch (CurOpSchemeType) {
    1883           0 :                 case DataPlant::OpScheme::Demand: //~ coils
    1884           0 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1885           0 :                     break;
    1886           0 :                 case DataPlant::OpScheme::Pump: //~ pump
    1887           0 :                     PumpLocation.loopNum = this->plantLoc.loopNum;
    1888           0 :                     PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
    1889           0 :                     PumpLocation.branchNum = BranchCounter;
    1890           0 :                     PumpLocation.compNum = CompCounter;
    1891           0 :                     if (this->BranchPumpsExist) {
    1892           0 :                         SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
    1893             :                     } else {
    1894           0 :                         SimulateSinglePump(state, PumpLocation, FlowRequest);
    1895             :                     }
    1896           0 :                     break;
    1897           0 :                 case OpScheme::HeatingRB:
    1898             :                 case OpScheme::CoolingRB: { //~ load range based
    1899           0 :                     ShowFatalError(state, "Encountered Load Based Object after other components, invalid.");
    1900           0 :                     break;
    1901             :                 }
    1902           0 :                 default:
    1903             :                     //~ Typical control equipment
    1904           0 :                     branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
    1905             :                 }
    1906             : 
    1907             :                 //~ If we didn't EXIT early, we must have simulated, so update array
    1908           0 :                 branch.lastComponentSimulated = CompCounter;
    1909             : 
    1910             :             } //~ CompCounter
    1911             : 
    1912           0 :             if (this->FlowLock == DataPlant::FlowLock::Locked) {
    1913           0 :                 PlantPressureSystem::SimPressureDropSystem(
    1914             :                     state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
    1915             :             }
    1916             : 
    1917             :         } //~ BranchCounter
    1918             : 
    1919             :         // I suppose I could do a check on the last component simulated to make sure we actually exhausted all branches
    1920             :         // This would be the "THIRD" check on flow validation, but would be OK
    1921             :     }
    1922             : 
    1923   425172404 :     void HalfLoopData::UpdateAnyLoopDemandAlterations(EnergyPlusData &state, int const BranchNum, int const CompNum)
    1924             :     {
    1925             : 
    1926             :         // SUBROUTINE INFORMATION:
    1927             :         //       AUTHOR         Edwin Lee
    1928             :         //       DATE WRITTEN   August 2010
    1929             :         //       MODIFIED       na
    1930             :         //       RE-ENGINEERED  na
    1931             : 
    1932             :         // PURPOSE OF THIS SUBROUTINE:
    1933             :         // This routine will analyze the given component and determine if any
    1934             :         //  alterations need to be made to the current loop demand value.  If so,
    1935             :         //  it will make the changes to the module level loop demand variables.
    1936             : 
    1937             :         // METHODOLOGY EMPLOYED:
    1938             :         // Components will always supply a useful delta T, even if it happens to be zero
    1939             :         // For flow rate, make decisions based on the component's current operating scheme type:
    1940             :         //    Demand based: these components will have a flow request on their inlet node
    1941             :         //    Pump: these components will not be included, as they no longer include heat at the pump
    1942             :         //    component setpoint: these components will have a flow request
    1943             : 
    1944             :         //    on their outlet node corresponding to their calculated delta T
    1945             :         //    load range based: these components do not 'alter' the load, they reject the load
    1946             :         //    Therefore they are not included
    1947             : 
    1948             :         // SUBROUTINE PARAMETER DEFINITIONS:
    1949             :         static constexpr std::string_view RoutineName("PlantLoopSolver::UpdateAnyLoopDemandAlterations");
    1950             : 
    1951             :         // Init to zero, so that if we don't find anything, we exit early
    1952   425172404 :         Real64 ComponentMassFlowRate(0.0);
    1953             : 
    1954   425172404 :         auto const &this_comp(this->Branch(BranchNum).Comp(CompNum));
    1955             : 
    1956             :         // Get information
    1957   425172404 :         int const InletNode(this_comp.NodeNumIn);
    1958   425172404 :         int const OutletNode(this_comp.NodeNumOut);
    1959             : 
    1960   425172404 :         if (this->FlowLock == DataPlant::FlowLock::Unlocked) {
    1961             : 
    1962   212586202 :             switch (this_comp.CurOpSchemeType) {
    1963           0 :             case OpScheme::HeatingRB:
    1964             :             case OpScheme::CoolingRB: { //~ load range based
    1965           0 :                 break;                  // Don't do anything for load based components
    1966             :             }
    1967             : 
    1968   212586202 :             default: {
    1969             :                 // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
    1970   212586202 :                 ComponentMassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
    1971             :                 // make sure components like economizers use the mass flow request
    1972   212586202 :                 break;
    1973             :             }
    1974             :             }
    1975             : 
    1976   212586202 :         } else if (this->FlowLock == DataPlant::FlowLock::Locked) {
    1977             : 
    1978             :             // For locked flow just use the mass flow rate
    1979             : 
    1980   212586202 :             switch (this_comp.CurOpSchemeType) {
    1981           0 :             case OpScheme::HeatingRB:
    1982             :             case OpScheme::CoolingRB: { //~ load range based
    1983           0 :                 break;                  // Don't do anything for load based components
    1984             :             }
    1985   212586202 :             default: {
    1986             :                 // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
    1987   212586202 :                 ComponentMassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
    1988             :             }
    1989             :             }
    1990             : 
    1991             :         } else { // flow pump query? problem?
    1992             :         }
    1993             : 
    1994             :         // Leave early if there wasn't a mass flow rate or request
    1995   425172404 :         if (ComponentMassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) return;
    1996             : 
    1997             :         // Get an average temperature for the property call
    1998   149140286 :         Real64 const InletTemp(state.dataLoopNodes->Node(InletNode).Temp);
    1999   149140286 :         Real64 const OutletTemp(state.dataLoopNodes->Node(OutletNode).Temp);
    2000   149140286 :         Real64 const AverageTemp((InletTemp + OutletTemp) / 2.0);
    2001   149140286 :         Real64 const ComponentCp(FluidProperties::GetSpecificHeatGlycol(state,
    2002   149140286 :                                                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    2003             :                                                                         AverageTemp,
    2004   149140286 :                                                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    2005             :                                                                         RoutineName));
    2006             : 
    2007             :         // Calculate the load altered by this component
    2008   149140286 :         Real64 const LoadAlteration(ComponentMassFlowRate * ComponentCp * (OutletTemp - InletTemp));
    2009             : 
    2010             :         // Now alter the module level variables
    2011   149140286 :         this->CurrentAlterationsToDemand += LoadAlteration;
    2012   149140286 :         this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint - this->CurrentAlterationsToDemand;
    2013             :     }
    2014             : 
    2015    37404504 :     void HalfLoopData::SimulateSinglePump(EnergyPlusData &state, PlantLocation const SpecificPumpLocation, Real64 &SpecificPumpFlowRate)
    2016             :     {
    2017             : 
    2018             :         // SUBROUTINE INFORMATION:
    2019             :         //       AUTHOR         Edwin Lee
    2020             :         //       DATE WRITTEN   July 2010
    2021             :         //       MODIFIED       na
    2022             :         //       RE-ENGINEERED  na
    2023             : 
    2024    37404504 :         auto &loop(state.dataPlnt->PlantLoop(SpecificPumpLocation.loopNum));
    2025    37404504 :         auto &loop_side(loop.LoopSide(SpecificPumpLocation.loopSideNum));
    2026    37404504 :         auto &loop_side_branch(loop_side.Branch(SpecificPumpLocation.branchNum));
    2027    37404504 :         auto &comp(loop_side_branch.Comp(SpecificPumpLocation.compNum));
    2028    37404504 :         int const PumpIndex = comp.IndexInLoopSidePumps;
    2029    37404504 :         auto &pump(loop_side.Pumps(PumpIndex));
    2030             : 
    2031    37404504 :         this->AdjustPumpFlowRequestByEMSControls(SpecificPumpLocation.branchNum, SpecificPumpLocation.compNum, SpecificPumpFlowRate);
    2032             : 
    2033             :         // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
    2034             :         bool DummyThisPumpRunning;
    2035    37404504 :         Pumps::SimPumps(state,
    2036    37404504 :                         pump.PumpName,
    2037    37404504 :                         SpecificPumpLocation.loopNum,
    2038             :                         SpecificPumpFlowRate,
    2039             :                         DummyThisPumpRunning,
    2040    37404504 :                         loop_side_branch.PumpIndex,
    2041    37404504 :                         pump.PumpHeatToFluid);
    2042             : 
    2043             :         //~ Pull some state information from the pump outlet node
    2044    37404504 :         pump.CurrentMinAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMinAvail;
    2045    37404504 :         pump.CurrentMaxAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMaxAvail;
    2046             : 
    2047             :         //~ Update the LoopSide pump heat totality here
    2048    37404504 :         if (loop_side.TotalPumps > 0) {
    2049    37404504 :             loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
    2050             :         }
    2051    37404504 :     }
    2052             : 
    2053    18526500 :     void HalfLoopData::SimulateAllLoopSidePumps(EnergyPlusData &state,
    2054             :                                                 ObjexxFCL::Optional<PlantLocation const> SpecificPumpLocation,
    2055             :                                                 ObjexxFCL::Optional<Real64 const> SpecificPumpFlowRate)
    2056             :     {
    2057             : 
    2058             :         // SUBROUTINE INFORMATION:
    2059             :         //       AUTHOR         Edwin Lee
    2060             :         //       DATE WRITTEN   July 2010
    2061             :         //       MODIFIED       na
    2062             :         //       RE-ENGINEERED  na
    2063             : 
    2064             :         int PumpIndexStart;
    2065             :         int PumpIndexEnd;
    2066             :         int PumpLoopNum;
    2067             :         DataPlant::LoopSideLocation PumpLoopSideNum;
    2068             : 
    2069             :         // If we have a specific loop/side/br/comp, then find the index and only do that one, otherwise do all pumps on the loop side
    2070    18526500 :         if (present(SpecificPumpLocation)) {
    2071           0 :             PumpLoopNum = SpecificPumpLocation().loopNum;
    2072           0 :             PumpLoopSideNum = SpecificPumpLocation().loopSideNum;
    2073           0 :             int const PumpBranchNum = SpecificPumpLocation().branchNum;
    2074           0 :             int const PumpCompNum = SpecificPumpLocation().compNum;
    2075           0 :             PumpIndexStart =
    2076           0 :                 state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum).Branch(PumpBranchNum).Comp(PumpCompNum).IndexInLoopSidePumps;
    2077           0 :             PumpIndexEnd = PumpIndexStart;
    2078             :         } else {
    2079    18526500 :             PumpLoopNum = this->plantLoc.loopNum;
    2080    18526500 :             PumpLoopSideNum = this->plantLoc.loopSideNum;
    2081    18526500 :             PumpIndexStart = 1;
    2082    18526500 :             PumpIndexEnd = this->TotalPumps;
    2083             :         }
    2084             : 
    2085             :         // If we have a flow rate to hit, then go for it, otherwise, just operate in request mode with zero flow
    2086             :         Real64 FlowToRequest;
    2087    18526500 :         if (present(SpecificPumpFlowRate)) {
    2088           0 :             FlowToRequest = SpecificPumpFlowRate;
    2089             :         } else {
    2090    18526500 :             FlowToRequest = 0.0;
    2091             :         }
    2092             : 
    2093             :         //~ Now loop through all the pumps and simulate them, keeping track of their status
    2094    18526500 :         auto &loop_side(state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum));
    2095    18526500 :         auto &loop_side_branch(loop_side.Branch);
    2096    37229965 :         for (int PumpCounter = PumpIndexStart; PumpCounter <= PumpIndexEnd; ++PumpCounter) {
    2097             : 
    2098             :             //~ Set some variables
    2099    18703465 :             auto &pump(loop_side.Pumps(PumpCounter));
    2100    18703465 :             int const PumpBranchNum = pump.BranchNum;
    2101    18703465 :             int const PumpCompNum = pump.CompNum;
    2102    18703465 :             int const PumpOutletNode = pump.PumpOutletNode;
    2103             : 
    2104    18703465 :             this->AdjustPumpFlowRequestByEMSControls(PumpBranchNum, PumpCompNum, FlowToRequest);
    2105             : 
    2106             :             // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
    2107             :             bool DummyThisPumpRunning;
    2108    18703465 :             Pumps::SimPumps(state,
    2109    18703465 :                             pump.PumpName,
    2110             :                             PumpLoopNum,
    2111             :                             FlowToRequest,
    2112             :                             DummyThisPumpRunning,
    2113    18703465 :                             loop_side_branch(PumpBranchNum).PumpIndex,
    2114    18703465 :                             pump.PumpHeatToFluid);
    2115             : 
    2116             :             //~ Pull some state information from the pump outlet node
    2117    18703465 :             Real64 const ThisPumpMinAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMinAvail;
    2118    18703465 :             Real64 const ThisPumpMaxAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMaxAvail;
    2119             : 
    2120             :             //~ Now update the data structure
    2121    18703465 :             pump.CurrentMinAvail = ThisPumpMinAvail;
    2122    18703465 :             pump.CurrentMaxAvail = ThisPumpMaxAvail;
    2123             :         }
    2124             : 
    2125             :         //~ Update the LoopSide pump heat totality here
    2126    18526500 :         if (loop_side.TotalPumps > 0) {
    2127    18525323 :             loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
    2128             :         }
    2129    18526500 :     }
    2130             : 
    2131    36285941 :     Real64 HalfLoopData::DetermineLoopSideFlowRate(EnergyPlusData &state, int ThisSideInletNode, Real64 ThisSideLoopFlowRequest)
    2132             :     {
    2133    36285941 :         Real64 ThisLoopSideFlow = ThisSideLoopFlowRequest;
    2134    36285941 :         Real64 TotalPumpMinAvailFlow = 0.0;
    2135    36285941 :         Real64 TotalPumpMaxAvailFlow = 0.0;
    2136    36285941 :         if (allocated(this->Pumps)) {
    2137             : 
    2138             :             //~ Initialize pump values
    2139    37226370 :             for (auto &e : this->Pumps) {
    2140    18702252 :                 e.CurrentMinAvail = 0.0;
    2141    18702252 :                 e.CurrentMaxAvail = 0.0;
    2142             :             }
    2143    18524118 :             this->FlowLock = DataPlant::FlowLock::PumpQuery;
    2144             : 
    2145             :             //~ Simulate pumps
    2146    18524118 :             this->SimulateAllLoopSidePumps(state);
    2147             : 
    2148             :             //~ Calculate totals
    2149    37226370 :             for (auto const &e : this->Pumps) {
    2150    18702252 :                 TotalPumpMinAvailFlow += e.CurrentMinAvail;
    2151    18702252 :                 TotalPumpMaxAvailFlow += e.CurrentMaxAvail;
    2152             :             }
    2153             : 
    2154             :             // Use the pump min/max avail to attempt to constrain the loop side flow
    2155    18524118 :             ThisLoopSideFlow = PlantUtilities::BoundValueToWithinTwoValues(ThisLoopSideFlow, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
    2156             :         }
    2157             : 
    2158             :         // Now we check flow restriction from the other side, both min and max avail.
    2159             :         // Doing this last basically means it wins, so the pump should pull down to meet the flow restriction
    2160    36285941 :         ThisLoopSideFlow = PlantUtilities::BoundValueToNodeMinMaxAvail(state, ThisLoopSideFlow, ThisSideInletNode);
    2161             : 
    2162             :         // Final preparation of loop inlet min/max avail if pumps exist
    2163    36285941 :         if (allocated(this->Pumps)) {
    2164             :             // At this point, the pump limits should have been obeyed unless a flow restriction was encountered from the other side
    2165             :             // The pump may, however, have even tighter constraints than the other side
    2166             :             // At this point, the inlet node doesn't know anything about those limits
    2167             :             // Since we have already honored the other side flow restriction, try to honor the pump limits here
    2168    18524118 :             PlantUtilities::TightenNodeMinMaxAvails(state, ThisSideInletNode, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
    2169             :         }
    2170             : 
    2171             :         // Now reset the entering mass flow rate to the decided-upon flow rate
    2172    36285941 :         state.dataLoopNodes->Node(ThisSideInletNode).MassFlowRate = ThisLoopSideFlow;
    2173    36285941 :         return ThisLoopSideFlow;
    2174             :     }
    2175             : 
    2176    72250166 :     void HalfLoopData::UpdatePlantMixer(EnergyPlusData &state)
    2177             :     {
    2178             : 
    2179             :         // SUBROUTINE INFORMATION:
    2180             :         //       AUTHOR         Brandon Anderson, Dan Fisher
    2181             :         //       DATE WRITTEN   October 1999
    2182             :         //       MODIFIED       na
    2183             :         //       RE-ENGINEERED  na
    2184             : 
    2185             :         // PURPOSE OF THIS SUBROUTINE:
    2186             :         // calculate the outlet conditions at the mixer
    2187             :         // this is expected to only be called for loops with a mixer
    2188             : 
    2189             :         // Find mixer outlet node number
    2190    72250166 :         int const MixerOutletNode = this->Mixer.NodeNumOut;
    2191             : 
    2192             :         // Find corresponding splitter inlet node number--correspondence, but currently
    2193             :         //  hard code things to a single split/mix setting it to the mixer number
    2194    72250166 :         int const SplitterInNode = this->Splitter.NodeNumIn;
    2195             :         // Initialize Mixer outlet temp and mass flow rate
    2196    72250166 :         Real64 MixerOutletTemp = 0.0;
    2197    72250166 :         Real64 MixerOutletMassFlow = 0.0;
    2198    72250166 :         Real64 MixerOutletMassFlowMaxAvail = 0.0;
    2199    72250166 :         Real64 MixerOutletMassFlowMinAvail = 0.0;
    2200    72250166 :         Real64 MixerOutletPress = 0.0;
    2201    72250166 :         Real64 MixerOutletQuality = 0.0;
    2202             : 
    2203             :         // Calculate Mixer outlet mass flow rate
    2204   388712420 :         for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
    2205   316462254 :             int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
    2206   316462254 :             MixerOutletMassFlow += state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
    2207             :         }
    2208             : 
    2209             :         // Calculate Mixer outlet temperature
    2210   252485644 :         for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
    2211   213702690 :             int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
    2212   213702690 :             if (MixerOutletMassFlow > 0.0) {
    2213   180235478 :                 Real64 const MixerInletMassFlow = state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
    2214   180235478 :                 Real64 const MassFrac = MixerInletMassFlow / MixerOutletMassFlow;
    2215             :                 // mass flow weighted temp and enthalpy for each mixer inlet
    2216   180235478 :                 MixerOutletTemp += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Temp;
    2217   180235478 :                 MixerOutletQuality += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Quality;
    2218   180235478 :                 MixerOutletMassFlowMaxAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMaxAvail;
    2219   180235478 :                 MixerOutletMassFlowMinAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMinAvail;
    2220   180235478 :                 MixerOutletPress = max(MixerOutletPress, state.dataLoopNodes->Node(MixerInletNode).Press);
    2221             :             } else { // MixerOutletMassFlow <=0, then perform the 'no flow' update.
    2222    33467212 :                 MixerOutletTemp = state.dataLoopNodes->Node(SplitterInNode).Temp;
    2223    33467212 :                 MixerOutletQuality = state.dataLoopNodes->Node(SplitterInNode).Quality;
    2224    33467212 :                 MixerOutletMassFlowMaxAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail;
    2225    33467212 :                 MixerOutletMassFlowMinAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail;
    2226    33467212 :                 MixerOutletPress = state.dataLoopNodes->Node(SplitterInNode).Press;
    2227    33467212 :                 break;
    2228             :             }
    2229             :         }
    2230             : 
    2231    72250166 :         state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate = MixerOutletMassFlow;
    2232    72250166 :         state.dataLoopNodes->Node(MixerOutletNode).Temp = MixerOutletTemp;
    2233    72250166 :         if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
    2234             :             // Don't update pressure, let pressure system handle this...
    2235             :         } else {
    2236             :             // Go ahead and update!
    2237    72208046 :             state.dataLoopNodes->Node(MixerOutletNode).Press = MixerOutletPress;
    2238             :         }
    2239    72250166 :         state.dataLoopNodes->Node(MixerOutletNode).Quality = MixerOutletQuality;
    2240             : 
    2241             :         // set max/min avails on mixer outlet to be consistent with the following rules
    2242             :         // 1.  limited by the max/min avails on splitter inlet
    2243             :         // 2.  limited by the sum of max/min avails for each branch's mixer inlet node
    2244             : 
    2245    72250166 :         state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMaxAvail =
    2246    72250166 :             min(MixerOutletMassFlowMaxAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail);
    2247    72250166 :         state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMinAvail =
    2248    72250166 :             max(MixerOutletMassFlowMinAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail);
    2249    72250166 :     }
    2250             : 
    2251    72250166 :     void HalfLoopData::UpdatePlantSplitter(EnergyPlusData &state)
    2252             :     {
    2253             : 
    2254             :         // SUBROUTINE INFORMATION:
    2255             :         //       AUTHOR         Brandon Anderson, Dan Fisher
    2256             :         //       DATE WRITTEN   October 1999
    2257             :         //       MODIFIED       na
    2258             :         //       RE-ENGINEERED  na
    2259             : 
    2260             :         // PURPOSE OF THIS SUBROUTINE:
    2261             :         // Set the outlet conditions of the splitter
    2262             : 
    2263             :         // Update Temperatures across splitter
    2264    72250166 :         if (this->Splitter.Exists) {
    2265             : 
    2266             :             // Set branch number at splitter inlet
    2267    72250166 :             int const SplitterInletNode = this->Splitter.NodeNumIn;
    2268             : 
    2269             :             // Loop over outlet nodes
    2270   388712420 :             for (int CurNode = 1; CurNode <= this->Splitter.TotalOutletNodes; ++CurNode) {
    2271   316462254 :                 int const SplitterOutletNode = this->Splitter.NodeNumOut(CurNode);
    2272             : 
    2273             :                 // Inlet Temp equals exit Temp to all outlet branches
    2274   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).Temp = state.dataLoopNodes->Node(SplitterInletNode).Temp;
    2275   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).TempMin = state.dataLoopNodes->Node(SplitterInletNode).TempMin;
    2276   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).TempMax = state.dataLoopNodes->Node(SplitterInletNode).TempMax;
    2277   316462254 :                 if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
    2278             :                     // Don't update pressure, let pressure system handle this...
    2279             :                 } else {
    2280             :                     // Go ahead and update!
    2281   316408434 :                     state.dataLoopNodes->Node(SplitterOutletNode).Press = state.dataLoopNodes->Node(SplitterInletNode).Press;
    2282             :                 }
    2283   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).Quality = state.dataLoopNodes->Node(SplitterInletNode).Quality;
    2284             : 
    2285             :                 // These two blocks and the following one which I added need to be cleaned up
    2286             :                 // I think we will always pass maxavail down the splitter, min avail is the issue.
    2287             :                 // Changed to include hardware max in next line
    2288   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMaxAvail = min(
    2289   316462254 :                     state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMax);
    2290   316462254 :                 state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail = 0.0;
    2291             : 
    2292             :                 // Not sure about passing min avail if it is nonzero.  I am testing a pump with nonzero
    2293             :                 // min flow rate, and it is causing problems because this routine passes zero down.  Perhaps if
    2294             :                 // it is a single parallel branch, we are safe to assume we need to just pass it down.
    2295             :                 // But need to test for multiple branches (or at least think about it), to see what we need to do...
    2296   316462254 :                 if (this->Splitter.TotalOutletNodes == 1) {
    2297     1680448 :                     state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail =
    2298     1680448 :                         state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMinAvail;
    2299             :                 }
    2300             :             }
    2301             :         }
    2302    72250166 :     }
    2303             : 
    2304             : } // namespace DataPlant
    2305             : } // namespace EnergyPlus

Generated by: LCOV version 1.14