LCOV - code coverage report
Current view: top level - EnergyPlus/Plant - LoopSide.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 84.2 % 976 822
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 20 20

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

Generated by: LCOV version 2.0-1