LCOV - code coverage report
Current view: top level - EnergyPlus/Plant - LoopSide.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 68.2 % 960 655
Test Date: 2025-05-22 16:09:37 Functions: 95.0 % 20 19

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

Generated by: LCOV version 2.0-1