LCOV - code coverage report
Current view: top level - EnergyPlus - PlantPressureSystem.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 234 315 74.3 %
Date: 2023-01-17 19:17:23 Functions: 11 11 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Array.functions.hh>
      53             : #include <ObjexxFCL/Array1D.hh>
      54             : #include <ObjexxFCL/Fmath.hh>
      55             : 
      56             : // EnergyPlus Headers
      57             : #include <EnergyPlus/CurveManager.hh>
      58             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59             : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
      60             : #include <EnergyPlus/DataEnvironment.hh>
      61             : #include <EnergyPlus/DataLoopNode.hh>
      62             : #include <EnergyPlus/FluidProperties.hh>
      63             : #include <EnergyPlus/OutputProcessor.hh>
      64             : #include <EnergyPlus/Plant/DataPlant.hh>
      65             : #include <EnergyPlus/PlantPressureSystem.hh>
      66             : #include <EnergyPlus/UtilityRoutines.hh>
      67             : 
      68             : namespace EnergyPlus::PlantPressureSystem {
      69             : 
      70             : // Module containing the routines dealing with the PlantPressureSystem simulation
      71             : 
      72             : // MODULE INFORMATION:
      73             : //       AUTHOR         Edwin Lee
      74             : //       DATE WRITTEN   August 2009
      75             : //       MODIFIED       February 2010: Add phase 2: loop flow correction
      76             : //       RE-ENGINEERED  na
      77             : 
      78             : // PURPOSE OF THIS MODULE:
      79             : // This module manages plant pressure-based simulations
      80             : 
      81             : // METHODOLOGY EMPLOYED:
      82             : // General EnergyPlus Methodology:
      83             : 
      84             : // OTHER NOTES:
      85             : //  Phase 1: Pump Power Correction: -Loop/Parallel flows are not resolved based on pressure drop
      86             : //                                  -Every flow path must see at least one branch with pressure information
      87             : //                                  -Pump power is updated based on the required pump head
      88             : //  Phase 2: Pump Flow Correction: -Loop flow resolved based on pump curve and loop pressure drop
      89             : //                                 -Parallel flows not resolved
      90             : //                                 -Every flow path must see at least one branch with pressure information
      91             : //                                 -Pump curve must be given also
      92             : //  Phase 3: Pressure Simulation: -Loop and parallel flows are resolved
      93             : //                                -All branches must have pressure information and pump must have pump curve
      94             : //                                -Not currently implemented
      95             : 
      96             : // Using/Aliasing
      97             : using namespace DataBranchAirLoopPlant;
      98             : 
      99   284668204 : void SimPressureDropSystem(EnergyPlusData &state,
     100             :                            int const LoopNum,                       // Plant Loop to update pressure information
     101             :                            bool const FirstHVACIteration,           // System flag
     102             :                            DataPlant::PressureCall const CallType,  // Enumerated call type
     103             :                            DataPlant::LoopSideLocation LoopSideNum, // Loop side num for specific branch simulation
     104             :                            Optional_int_const BranchNum             // Branch num for specific branch simulation
     105             : )
     106             : {
     107             : 
     108             :     // SUBROUTINE INFORMATION:
     109             :     //       AUTHOR         Edwin Lee
     110             :     //       DATE WRITTEN   August 2009
     111             :     //       MODIFIED       na
     112             :     //       RE-ENGINEERED  na
     113             : 
     114             :     // PURPOSE OF THIS SUBROUTINE:
     115             :     // This routine is the public interface for pressure system simulation
     116             :     // Calls are made to private components as needed
     117             : 
     118             :     // METHODOLOGY EMPLOYED:
     119             :     // Standard EnergyPlus methodology
     120             : 
     121             :     // Using/Aliasing
     122             : 
     123             :     // Exit out of any calculation routines if we don't do pressure simulation for this loop
     124   552034340 :     if ((state.dataPlnt->PlantLoop(LoopNum).PressureSimType == DataPlant::PressSimType::NoPressure) &&
     125    34395351 :         ((CallType == DataPlant::PressureCall::Calc) || (CallType == DataPlant::PressureCall::Update)))
     126   267366136 :         return;
     127             : 
     128             :     // Pass to another routine based on calling flag
     129    17302068 :     switch (CallType) {
     130    17211978 :     case DataPlant::PressureCall::Init: {
     131    17211978 :         InitPressureDrop(state, LoopNum, FirstHVACIteration);
     132    17211978 :     } break;
     133       79560 :     case DataPlant::PressureCall::Calc: {
     134       79560 :         BranchPressureDrop(state, LoopNum, LoopSideNum, BranchNum); // Autodesk:OPTIONAL LoopSideNum, BranchNum used without PRESENT check
     135       79560 :     } break;
     136       10530 :     case DataPlant::PressureCall::Update: {
     137       10530 :         UpdatePressureDrop(state, LoopNum);
     138       10530 :     } break;
     139           0 :     default: {
     140             :         // Calling routines should only use the three possible keywords here
     141           0 :     } break;
     142             :     }
     143             : }
     144             : 
     145    17211978 : void InitPressureDrop(EnergyPlusData &state, int const LoopNum, bool const FirstHVACIteration)
     146             : {
     147             : 
     148             :     // SUBROUTINE INFORMATION:
     149             :     //       AUTHOR         Edwin Lee
     150             :     //       DATE WRITTEN   August 2009
     151             :     //       MODIFIED       na
     152             :     //       RE-ENGINEERED  na
     153             : 
     154             :     // PURPOSE OF THIS SUBROUTINE:
     155             :     // Initializes output variables and data structure
     156             :     // On FirstHVAC, updates the demand inlet node pressure
     157             : 
     158             :     // Using/Aliasing
     159             : 
     160             :     // Simulation Variables
     161             :     int NumBranches;
     162             :     int BranchPressureTally;
     163             : 
     164    17211978 :     if (state.dataPlantPressureSys->InitPressureDropOneTimeInit) {
     165             :         // First allocate the initialization array to each plant loop
     166         442 :         state.dataPlantPressureSys->LoopInit.allocate(size(state.dataPlnt->PlantLoop));
     167         442 :         state.dataPlantPressureSys->LoopInit = true;
     168         442 :         state.dataPlantPressureSys->InitPressureDropOneTimeInit = false;
     169             :     }
     170             : 
     171    17211978 :     auto &loop(state.dataPlnt->PlantLoop(LoopNum));
     172             : 
     173             :     // CurrentModuleObject='Curve:Functional:PressureDrop'
     174    17211978 :     if (state.dataPlantPressureSys->LoopInit(LoopNum)) {
     175             : 
     176             :         // Initialize
     177        1107 :         bool ErrorsFound(false);
     178        1107 :         bool SeriesPressureComponentFound(false);
     179             : 
     180             :         // Need to go along plant loop and set up component pressure drop data structure!
     181        3321 :         for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
     182        2214 :             auto &loop_side(loop.LoopSide(LoopSideNum));
     183             : 
     184             :             // Loop through all branches on this loop side
     185       15076 :             for (int BranchNum = 1; BranchNum <= isize(loop_side.Branch); ++BranchNum) {
     186       12862 :                 auto &branch(loop_side.Branch(BranchNum));
     187             : 
     188             :                 // If this branch has valid pressure drop data
     189       12862 :                 if (branch.PressureCurveIndex > 0) {
     190             : 
     191             :                     // Update flags for higher level structure
     192           4 :                     branch.HasPressureComponents = true;
     193           4 :                     loop_side.HasPressureComponents = true;
     194           4 :                     loop.HasPressureComponents = true;
     195             : 
     196             :                     // Setup output variable
     197           8 :                     SetupOutputVariable(state,
     198             :                                         "Plant Branch Pressure Difference",
     199             :                                         OutputProcessor::Unit::Pa,
     200             :                                         branch.PressureDrop,
     201             :                                         OutputProcessor::SOVTimeStepType::Plant,
     202             :                                         OutputProcessor::SOVStoreType::Average,
     203           4 :                                         branch.Name);
     204             :                 }
     205             :             }
     206             : 
     207             :             // Set up LoopSide level variables if applicable
     208        2214 :             if (loop_side.HasPressureComponents) {
     209           4 :                 if (LoopSideNum == DataPlant::LoopSideLocation::Demand) {
     210             : 
     211           2 :                     SetupOutputVariable(state,
     212             :                                         "Plant Demand Side Loop Pressure Difference",
     213             :                                         OutputProcessor::Unit::Pa,
     214             :                                         loop_side.PressureDrop,
     215             :                                         OutputProcessor::SOVTimeStepType::Plant,
     216             :                                         OutputProcessor::SOVStoreType::Average,
     217           1 :                                         loop.Name);
     218             : 
     219           3 :                 } else if (LoopSideNum == DataPlant::LoopSideLocation::Supply) {
     220             : 
     221           6 :                     SetupOutputVariable(state,
     222             :                                         "Plant Supply Side Loop Pressure Difference",
     223             :                                         OutputProcessor::Unit::Pa,
     224             :                                         loop_side.PressureDrop,
     225             :                                         OutputProcessor::SOVTimeStepType::Plant,
     226             :                                         OutputProcessor::SOVStoreType::Average,
     227           3 :                                         loop.Name);
     228             :                 }
     229             :             }
     230             :         }
     231             : 
     232        1107 :         if (loop.HasPressureComponents) {
     233           3 :             state.dataPlantPressureSys->FullParallelBranchSetFound[static_cast<int>(DataPlant::LoopSideLocation::Demand)] =
     234           3 :                 state.dataPlantPressureSys->FullParallelBranchSetFound[static_cast<int>(DataPlant::LoopSideLocation::Supply)] = false;
     235             : 
     236             :             // Set up loop level variables if applicable
     237             : 
     238           6 :             SetupOutputVariable(state,
     239             :                                 "Plant Loop Pressure Difference",
     240             :                                 OutputProcessor::Unit::Pa,
     241             :                                 loop.PressureDrop,
     242             :                                 OutputProcessor::SOVTimeStepType::Plant,
     243             :                                 OutputProcessor::SOVStoreType::Average,
     244           3 :                                 loop.Name);
     245             : 
     246             :             // Check for illegal configurations on this plant loop
     247           9 :             for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
     248             :                 // Check for illegal parallel branch setups
     249           6 :                 auto &loop_side(loop.LoopSide(LoopSideNum));
     250           6 :                 BranchPressureTally = 0;
     251           6 :                 NumBranches = size(loop_side.Branch);
     252           6 :                 if (NumBranches > 2) {
     253          13 :                     for (int BranchNum = 2; BranchNum <= NumBranches - 1; ++BranchNum) {
     254           7 :                         if (loop_side.Branch(BranchNum).HasPressureComponents) {
     255           1 :                             loop_side.HasParallelPressComps = true;
     256           1 :                             ++BranchPressureTally;
     257             :                         }
     258             :                     }
     259             :                 }
     260           6 :                 if (BranchPressureTally == 0) {
     261             :                     // no parallel branches, ok for this check
     262           1 :                 } else if (BranchPressureTally == isize(loop_side.Branch) - 2) {
     263             :                     // all parallel branches have pressure components
     264           1 :                     state.dataPlantPressureSys->FullParallelBranchSetFound[static_cast<int>(LoopSideNum)] = true;
     265             :                 } else {
     266             :                     // we aren't ok
     267           0 :                     ShowSevereError(state, "Pressure drop component configuration error detected on loop: " + loop.Name);
     268           0 :                     ShowContinueError(state, "Pressure drop components must be on ALL or NONE of the parallel branches.");
     269           0 :                     ShowContinueError(state, "Partial distribution is not allowed.");
     270           0 :                     ErrorsFound = true;
     271             :                 }
     272           6 :                 if (loop_side.Branch(1).HasPressureComponents || loop_side.Branch(NumBranches).HasPressureComponents) {
     273             :                     // we have a series component pressure branch (whether a single branch half loop or mixer/splitter setup
     274           3 :                     SeriesPressureComponentFound = true;
     275             :                 }
     276             :             }
     277             : 
     278             :             // Check for full path pressure data
     279           9 :             if (state.dataPlantPressureSys->FullParallelBranchSetFound[static_cast<int>(DataPlant::LoopSideLocation::Demand)] ||
     280           5 :                 state.dataPlantPressureSys->FullParallelBranchSetFound[static_cast<int>(DataPlant::LoopSideLocation::Supply)] ||
     281             :                 SeriesPressureComponentFound) {
     282             :                 // we are fine, either way we will always have a path with at least one pressure component hit
     283             :             } else {
     284           0 :                 ShowSevereError(state, "Pressure drop component configuration error detected on loop: " + loop.Name);
     285           0 :                 ShowContinueError(state, "The loop has at least one fluid path which does not encounter a pressure component.");
     286           0 :                 ShowContinueError(state, "Either use at least one serial component for pressure drop OR all possible parallel paths");
     287           0 :                 ShowContinueError(state, "must be pressure drop components.");
     288           0 :                 ErrorsFound = true;
     289             :             } // valid pressure path
     290             : 
     291             :         } // Has pressure components
     292             : 
     293        1107 :         if (ErrorsFound) ShowFatalError(state, "Preceding errors cause program termination");
     294             : 
     295             :         // Also issue one time warning if there is a mismatch between plant loop simulation type and whether objects were entered
     296        1107 :         if (loop.HasPressureComponents && (loop.PressureSimType == DataPlant::PressSimType::NoPressure)) {
     297             :             // Then we found pressure components on the branches, but the plant loop said it didn't want to do pressure simulation
     298           0 :             ShowWarningError(state, "Error for pressure simulation on plant loop: " + loop.Name);
     299           0 :             ShowContinueError(state, "Plant loop contains pressure simulation components on the branches,");
     300           0 :             ShowContinueError(state, " yet in the PlantLoop object, there is no pressure simulation specified.");
     301           0 :             ShowContinueError(state, "Simulation continues, ignoring pressure simulation data.");
     302        1107 :         } else if ((!loop.HasPressureComponents) && (loop.PressureSimType != DataPlant::PressSimType::NoPressure)) {
     303             :             // Then we don't have any pressure components on the branches, yet the plant loop wants to do some sort of pressure simulation
     304           0 :             ShowWarningError(state, "Error for pressure simulation on plant loop: " + loop.Name);
     305           0 :             ShowContinueError(state, "Plant loop is requesting a pressure simulation,");
     306           0 :             ShowContinueError(state, " yet there are no pressure simulation components detected on any of the branches in that loop.");
     307           0 :             ShowContinueError(state, "Simulation continues, ignoring pressure simulation data.");
     308             :         }
     309             : 
     310        1107 :         state.dataPlantPressureSys->LoopInit(LoopNum) = false;
     311             : 
     312             :     } // LoopInit = TRUE
     313             : 
     314             :     // Initialize the entire plant loop to the outdoor pressure if that loop has data
     315             :     // This value at the demand side outlet node will be used as a starting reference point
     316             :     // for pressure calcs
     317             :     // The value is smeared across the loop, however, so that any nodes before a pump will
     318             :     // have a proper value for pressure
     319    17211978 :     if (loop.HasPressureComponents && FirstHVACIteration) {
     320       15795 :         for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
     321       10530 :             auto const &loop_side(loop.LoopSide(LoopSideNum));
     322       45045 :             for (int BranchNum = 1, BranchNum_end = isize(loop_side.Branch); BranchNum <= BranchNum_end; ++BranchNum) {
     323       34515 :                 auto const &branch(loop_side.Branch(BranchNum));
     324       69030 :                 for (int CompNum = 1, CompNum_end = isize(branch.Comp); CompNum <= CompNum_end; ++CompNum) {
     325       34515 :                     auto const &component(branch.Comp(CompNum));
     326       34515 :                     state.dataLoopNodes->Node(component.NodeNumIn).Press = state.dataEnvrn->StdBaroPress;
     327       34515 :                     state.dataLoopNodes->Node(component.NodeNumOut).Press = state.dataEnvrn->StdBaroPress;
     328             :                 }
     329             :             }
     330             :         }
     331             :     }
     332             : 
     333             :     // Now tell the pump routine whether or not to use the pressure data to calculate power
     334    17211978 :     if (loop.HasPressureComponents) {
     335       10530 :         loop.UsePressureForPumpCalcs = !FirstHVACIteration;
     336             :     } else { // No Pressure Components
     337    17201448 :         loop.UsePressureForPumpCalcs = false;
     338             :     }
     339             : 
     340             :     // Before we leave, override any settings in case we are doing common pipe simulation
     341    17211978 :     if (loop.HasPressureComponents) {
     342             :         // We need to make sure we aren't doing an invalid configuration here
     343       10530 :         if (loop.CommonPipeType != DataPlant::CommonPipeType::No) {
     344             :             // There is a common pipe!
     345           0 :             if (!state.dataPlantPressureSys->CommonPipeErrorEncountered) {
     346           0 :                 ShowSevereError(state, "Invalid pressure simulation configuration for Plant Loop=" + loop.Name);
     347           0 :                 ShowContinueError(state, "Currently pressure simulations cannot be performed for loops with common pipes.");
     348           0 :                 ShowContinueError(state, "To repair, either remove the common pipe simulation, or remove the pressure simulation.");
     349           0 :                 ShowContinueError(state, "The simulation will continue, but the pump power is not updated with pressure drop data.");
     350           0 :                 ShowContinueError(state, "Check all results including node pressures to ensure proper simulation.");
     351           0 :                 ShowContinueError(state, "This message is reported once, but may have been encountered in multiple loops.");
     352           0 :                 state.dataPlantPressureSys->CommonPipeErrorEncountered = true;
     353             :             }
     354           0 :             loop.UsePressureForPumpCalcs = false;
     355             :         }
     356             :     }
     357    17211978 : }
     358             : 
     359       79560 : void BranchPressureDrop(EnergyPlusData &state,
     360             :                         int const LoopNum,                             // Plant Loop Index
     361             :                         const DataPlant::LoopSideLocation LoopSideNum, // LoopSide on Plant Loop LoopNum
     362             :                         int const BranchNum                            // Branch Index on LoopSide LoopSideNum
     363             : )
     364             : {
     365             : 
     366             :     // SUBROUTINE INFORMATION:
     367             :     //       AUTHOR         Edwin Lee
     368             :     //       DATE WRITTEN   August 2009
     369             :     //       MODIFIED       na
     370             :     //       RE-ENGINEERED  na
     371             : 
     372             :     // PURPOSE OF THIS SUBROUTINE:
     373             :     // This will choose an appropriate pressure drop calculation routine based on structure flags
     374             : 
     375             :     // Using/Aliasing
     376             :     using Curve::CurveValue;
     377             :     using Curve::PressureCurveValue;
     378             :     using FluidProperties::GetDensityGlycol;
     379             :     using FluidProperties::GetViscosityGlycol;
     380             : 
     381             :     // SUBROUTINE PARAMETER DEFINITIONS:
     382             :     static constexpr std::string_view RoutineName("CalcPlantPressureSystem");
     383             : 
     384             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     385             :     int FluidIndex;                                              // Plant loop level Fluid Index
     386             :     int InletNodeNum;                                            // Component inlet node number
     387             :     DataBranchAirLoopPlant::PressureCurveType pressureCurveType; // Type of curve used to evaluate pressure drop
     388             :     int PressureCurveIndex;                                      // Curve index for PerfCurve structure
     389             :     Real64 NodeMassFlow;                                         // Nodal mass flow rate {kg/s}
     390             :     Real64 NodeTemperature;                                      // Nodal temperature {C}
     391             :     Real64 NodeDensity;                                          // Nodal density {kg/m3}
     392             :     Real64 NodeViscosity;                                        // Nodal viscosity, assuming mu here (dynamic viscosity)
     393       79560 :     Real64 BranchDeltaPress(0.0);                                // Pressure drop for component, {Pa}
     394             : 
     395             :     // Exit early if need be
     396       79560 :     if (!state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).HasPressureComponents) {
     397       64350 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureDrop = 0.0;
     398       64350 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK = 0.0;
     399       64350 :         return;
     400             :     }
     401             : 
     402             :     // Get data from data structure
     403       15210 :     FluidIndex = state.dataPlnt->PlantLoop(LoopNum).FluidIndex;
     404       15210 :     InletNodeNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumIn;
     405       15210 :     pressureCurveType = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureCurveType;
     406       15210 :     PressureCurveIndex = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureCurveIndex;
     407             : 
     408             :     // Get nodal conditions
     409       15210 :     NodeMassFlow = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
     410       15210 :     NodeTemperature = state.dataLoopNodes->Node(InletNodeNum).Temp;
     411       15210 :     NodeDensity = GetDensityGlycol(state, std::string(), NodeTemperature, FluidIndex, RoutineName);
     412       15210 :     NodeViscosity = GetViscosityGlycol(state, std::string(), NodeTemperature, FluidIndex, RoutineName);
     413             : 
     414             :     // Call the appropriate pressure calculation routine
     415       15210 :     switch (pressureCurveType) {
     416       12870 :     case DataBranchAirLoopPlant::PressureCurveType::Pressure: {
     417             :         // DeltaP = [f*(L/D) + K] * (rho * V^2) / 2
     418       12870 :         BranchDeltaPress = PressureCurveValue(state, PressureCurveIndex, NodeMassFlow, NodeDensity, NodeViscosity);
     419       12870 :     } break;
     420        2340 :     case DataBranchAirLoopPlant::PressureCurveType::Generic: {
     421             :         // DeltaP = func(mdot)
     422             :         // Generic curve, only pass V1=mass flow rate
     423        2340 :         BranchDeltaPress = CurveValue(state, PressureCurveIndex, NodeMassFlow);
     424        2340 :     } break;
     425           0 :     default: {
     426             :         // Shouldn't end up here, but just in case
     427           0 :         ++state.dataPlantPressureSys->ErrorCounter;
     428           0 :         if (state.dataPlantPressureSys->ErrorCounter == 1) {
     429           0 :             ShowSevereError(state, "Plant pressure simulation encountered a branch which contains invalid branch pressure curve type.");
     430           0 :             ShowContinueError(state, "Occurs for branch: " + state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Name);
     431           0 :             ShowContinueError(state, "This error will be issued only once, although other branches may encounter the same problem");
     432           0 :             ShowContinueError(state, "For now, pressure drop on this branch will be set to zero.");
     433           0 :             ShowContinueError(state, "Verify all pressure inputs and pressure drop output variables to ensure proper simulation");
     434             :         }
     435           0 :     } break;
     436             :     }
     437             : 
     438             :     // Log this pressure in the data structure to be handled by the update routine later
     439       15210 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureDrop = BranchDeltaPress;
     440             : 
     441             :     // Update the effective K-value for this branch
     442       15210 :     if (NodeMassFlow > 0.0) {
     443       14550 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK = BranchDeltaPress / pow_2(NodeMassFlow);
     444             :     } else {
     445         660 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK = 0.0;
     446             :     }
     447             : }
     448             : 
     449       10530 : void UpdatePressureDrop(EnergyPlusData &state, int const LoopNum)
     450             : {
     451             : 
     452             :     // SUBROUTINE INFORMATION:
     453             :     //       AUTHOR         Edwin Lee
     454             :     //       DATE WRITTEN   August 2009
     455             :     //       MODIFIED       na
     456             :     //       RE-ENGINEERED  na
     457             : 
     458             :     // PURPOSE OF THIS SUBROUTINE:
     459             :     // Evaluate the pressure drop across an entire plant loop and places the value
     460             :     // on the PlantLoop(:) data structure for the pump to use
     461             : 
     462             :     // METHODOLOGY EMPLOYED:
     463             :     // Assumes that the supply inlet is the starting node, which will be set to some standard pressure
     464             :     // Then we move around the loop backward from this reference point and go until we hit a pump and stop.
     465             :     // The pressure difference from reference to pump is the new required pump head.
     466             : 
     467             :     // Using/Aliasing
     468             : 
     469             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     470             :     int BranchNum;
     471             :     int NumBranches;
     472             :     Real64 BranchPressureDrop;
     473             :     Real64 LoopSidePressureDrop;
     474             :     Real64 LoopPressureDrop;
     475       21060 :     Array1D<Real64> ParallelBranchPressureDrops;
     476       21060 :     Array1D<Real64> ParallelBranchInletPressures;
     477             :     int ParallelBranchCounter;
     478             :     Real64 SplitterInletPressure;
     479             :     Real64 MixerPressure;
     480             :     bool FoundAPumpOnBranch;
     481             :     Real64 EffectiveLoopKValue;
     482             :     Real64 EffectiveLoopSideKValue;
     483             :     Real64 TempVal_SumOfOneByRootK;
     484             : 
     485             :     // Exit if not needed
     486       10530 :     if (!state.dataPlnt->PlantLoop(LoopNum).HasPressureComponents) return;
     487             : 
     488             :     // Now go through and update the pressure drops as needed
     489       10530 :     FoundAPumpOnBranch = false;
     490       10530 :     LoopPressureDrop = 0.0;
     491       31590 :     for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) { // Start at demand side outlet
     492             : 
     493             :         // Loop through all branches on this loop side
     494       21060 :         LoopSidePressureDrop = 0.0;
     495       21060 :         NumBranches = size(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch);
     496             : 
     497             :         // Split here based on a single branch loop or a splitter/mixer configuration
     498       21060 :         if (NumBranches == 1) { // Just do the single branch
     499             : 
     500             :             //***SINGLE BRANCH***!
     501           0 :             BranchNum = 1;
     502           0 :             DistributePressureOnBranch(state, LoopNum, LoopSideNum, BranchNum, BranchPressureDrop, FoundAPumpOnBranch);
     503           0 :             LoopSidePressureDrop += BranchPressureDrop;
     504           0 :             LoopPressureDrop += BranchPressureDrop;
     505             :             //*******************!
     506             : 
     507       21060 :         } else if (NumBranches > 1) { // Loop through all branches on this loop side, mixer/splitter configuration
     508             : 
     509             :             //***OUTLET BRANCH***!
     510       21060 :             BranchNum = NumBranches;
     511       21060 :             DistributePressureOnBranch(state, LoopNum, LoopSideNum, BranchNum, BranchPressureDrop, FoundAPumpOnBranch);
     512       21060 :             LoopSidePressureDrop += BranchPressureDrop;
     513       21060 :             LoopPressureDrop += BranchPressureDrop;
     514             :             //*******************!
     515             : 
     516             :             //***MIXER SIMULATION***!
     517       21060 :             MixerPressure = state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumIn).Press;
     518       21060 :             PassPressureAcrossMixer(state, LoopNum, LoopSideNum, MixerPressure, NumBranches);
     519             :             //**********************!
     520             : 
     521             :             //***PARALLEL BRANCHES***!
     522       21060 :             if (allocated(ParallelBranchPressureDrops)) ParallelBranchPressureDrops.deallocate();
     523       21060 :             ParallelBranchPressureDrops.allocate(NumBranches - 2);
     524       21060 :             if (allocated(ParallelBranchInletPressures)) ParallelBranchInletPressures.deallocate();
     525       21060 :             ParallelBranchInletPressures.allocate(NumBranches - 2);
     526       21060 :             ParallelBranchCounter = 0;
     527             : 
     528             :             // Reset Pump found flag to false, to check if actually found on one of the parallel branches
     529       21060 :             FoundAPumpOnBranch = false;
     530       47970 :             for (BranchNum = NumBranches - 1; BranchNum >= 2; --BranchNum) { // Working backward (not necessary, but consistent)
     531       26910 :                 ++ParallelBranchCounter;
     532       26910 :                 DistributePressureOnBranch(
     533       26910 :                     state, LoopNum, LoopSideNum, BranchNum, ParallelBranchPressureDrops(ParallelBranchCounter), FoundAPumpOnBranch);
     534             :                 // Store the branch inlet pressure so we can pass it properly across the splitter
     535       26910 :                 ParallelBranchInletPressures(ParallelBranchCounter) =
     536       26910 :                     state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumIn).Press;
     537             :             }
     538             : 
     539             :             // Now take max inlet pressure to pass across splitter and max branch pressure for bookkeeping
     540       21060 :             SplitterInletPressure = maxval(ParallelBranchInletPressures);
     541       21060 :             BranchPressureDrop = maxval(ParallelBranchPressureDrops);
     542       21060 :             LoopSidePressureDrop += BranchPressureDrop;
     543       21060 :             LoopPressureDrop += BranchPressureDrop;
     544             :             //**********************!
     545             : 
     546             :             // If we found pumps on the parallel branches then we are done,
     547             :             // If we are on the demand side, we have a common pipe situation and should issue a warning
     548       21060 :             if (FoundAPumpOnBranch) {
     549           0 :                 if (LoopSideNum == DataPlant::LoopSideLocation::Demand) {
     550           0 :                     ShowSevereError(state, "Pressure system information was found in a demand pump (common pipe) simulation");
     551           0 :                     ShowContinueError(state, "Currently the pressure simulation is not set up to handle common pipe simulations");
     552           0 :                     ShowContinueError(state, "Either modify simulation to avoid common pipe, or remove pressure curve information");
     553           0 :                     ShowFatalError(state, "Pressure configuration mismatch causes program termination");
     554             :                 }
     555             :                 // If we are on the supply side, we simply hit the branch pump, so we exit the IF statement as
     556             :                 //  we don't need to simulate the splitter or inlet branch
     557             :                 // For now, not doing anything will leave the IF block
     558             :             }
     559             : 
     560             :             // If we haven't found a pump on the parallel branches then we need to go ahead
     561             :             // and simulate the splitter and inlet branch
     562             : 
     563             :             // This may all be superfluous, if we just simulate the splitter and inlet branch we may be fine
     564             :             // even if there were branch pumps found.
     565       21060 :             if (!FoundAPumpOnBranch) {
     566             : 
     567             :                 //***SPLITTER SIMULATION***!
     568       21060 :                 PassPressureAcrossSplitter(state, LoopNum, LoopSideNum, SplitterInletPressure);
     569             :                 //*************************!
     570             : 
     571             :                 //***INLET BRANCH***!
     572       21060 :                 BranchNum = 1;
     573       21060 :                 DistributePressureOnBranch(state, LoopNum, LoopSideNum, BranchNum, BranchPressureDrop, FoundAPumpOnBranch);
     574       21060 :                 LoopSidePressureDrop += BranchPressureDrop;
     575       21060 :                 LoopPressureDrop += BranchPressureDrop;
     576             :                 //******************!
     577             : 
     578             :                 //***PLANT INTERFACE***!
     579       21060 :                 if (LoopSideNum == DataPlant::LoopSideLocation::Demand) {
     580       10530 :                     PassPressureAcrossInterface(state, LoopNum);
     581             :                 }
     582             :                 //*********************!
     583             :             }
     584             :         }
     585             : 
     586       21060 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).PressureDrop = LoopSidePressureDrop;
     587             : 
     588             :     } // LoopSides on this loop
     589             : 
     590       10530 :     state.dataPlnt->PlantLoop(LoopNum).PressureDrop = LoopPressureDrop;
     591             : 
     592             :     // Now do effective K value calculations
     593       10530 :     EffectiveLoopKValue = 0.0;
     594             : 
     595       31590 :     for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
     596             : 
     597       21060 :         EffectiveLoopSideKValue = 0.0;
     598             : 
     599             :         // Always take the first branch K, it may be the only branch on this half loop
     600       21060 :         EffectiveLoopSideKValue += state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(1).PressureEffectiveK;
     601             : 
     602             :         // If there is only one branch then move to the other loop side
     603       21060 :         if (size(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch) == 1) continue;
     604             : 
     605             :         // Add parallel branches if necessary by adding them as SUM(1/(sqrt(K_i)))
     606       21060 :         TempVal_SumOfOneByRootK = 0.0;
     607       47970 :         for (BranchNum = 2; BranchNum <= isize(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch) - 1; ++BranchNum) {
     608             : 
     609             :             // Only add this branch if the K value is non-zero
     610       26910 :             if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK > 0.0) {
     611        2328 :                 TempVal_SumOfOneByRootK +=
     612        2328 :                     (1.0 / std::sqrt(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK));
     613             :             }
     614             :         }
     615             : 
     616             :         // Add parallel branches if they are greater than zero, by taking the sum and performing (1/(SUM^2))
     617       21060 :         if (TempVal_SumOfOneByRootK > 0.0) EffectiveLoopSideKValue += (1.0 / pow_2(TempVal_SumOfOneByRootK));
     618             : 
     619             :         // Always take the last branch K, it will be in series
     620       21060 :         BranchNum = size(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch);
     621       21060 :         EffectiveLoopSideKValue += state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureEffectiveK;
     622             : 
     623             :         // Assign this loop side's K-value
     624       21060 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).PressureEffectiveK = EffectiveLoopSideKValue;
     625             : 
     626             :         // Keep adding the overall loop K-value
     627       21060 :         EffectiveLoopKValue += EffectiveLoopSideKValue;
     628             :     }
     629             : 
     630             :     // Assign this loop's K-value
     631       10530 :     state.dataPlnt->PlantLoop(LoopNum).PressureEffectiveK = EffectiveLoopKValue;
     632             : }
     633             : 
     634       69030 : void DistributePressureOnBranch(EnergyPlusData &state,
     635             :                                 int const LoopNum,
     636             :                                 const DataPlant::LoopSideLocation LoopSideNum,
     637             :                                 int const BranchNum,
     638             :                                 Real64 &BranchPressureDrop,
     639             :                                 bool &PumpFound)
     640             : {
     641             : 
     642             :     // SUBROUTINE INFORMATION:
     643             :     //       AUTHOR         Edwin Lee
     644             :     //       DATE WRITTEN   August 2009
     645             :     //       MODIFIED       na
     646             :     //       RE-ENGINEERED  na
     647             : 
     648             :     // PURPOSE OF THIS SUBROUTINE:
     649             :     // Apply proper pressure to nodes along branch
     650             : 
     651             :     // METHODOLOGY EMPLOYED:
     652             :     // Move backward through components, passing pressure upstream
     653             :     // Account for branch pressure drop at branch inlet node
     654             :     // Update PlantLoop(:)%LoopSide(:)%Branch(:)%PressureDrop Variable
     655             : 
     656             :     // Using/Aliasing
     657             : 
     658             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     659             :     int CompNum;
     660             :     int NumCompsOnBranch;
     661             :     Real64 TempBranchPressureDrop;
     662             : 
     663             :     // Initialize
     664       69030 :     TempBranchPressureDrop = 0.0;
     665       69030 :     BranchPressureDrop = 0.0;
     666       69030 :     NumCompsOnBranch = size(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp);
     667             : 
     668             :     // Retrieve temporary branch pressure drop
     669       69030 :     if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).HasPressureComponents) {
     670       12870 :         TempBranchPressureDrop = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).PressureDrop;
     671             :     }
     672             : 
     673             :     // If the last component on the branch is the pump, then check if a pressure drop is detected and set the flag and leave
     674       69030 :     if (DataPlant::PlantEquipmentTypeIsPump[static_cast<int>(
     675       69030 :             state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(NumCompsOnBranch).Type)]) {
     676       10530 :         PumpFound = true;
     677       10530 :         if (TempBranchPressureDrop != 0.0) {
     678           0 :             ShowSevereError(state, "Error in plant pressure simulation for plant loop: " + state.dataPlnt->PlantLoop(LoopNum).Name);
     679           0 :             if (LoopSideNum == DataPlant::LoopSideLocation::Demand) {
     680           0 :                 ShowContinueError(
     681           0 :                     state, "Occurs for demand side, branch: " + state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Name);
     682           0 :             } else if (LoopSideNum == DataPlant::LoopSideLocation::Supply) {
     683           0 :                 ShowContinueError(
     684           0 :                     state, "Occurs for supply side, branch: " + state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Name);
     685             :             }
     686           0 :             ShowContinueError(state, "Branch contains only a single pump component, yet also a pressure drop component.");
     687           0 :             ShowContinueError(state, "Either add a second component to this branch after the pump, or move pressure drop data.");
     688           0 :             ShowFatalError(state, "Preceding pressure drop error causes program termination");
     689             :         }
     690       10530 :         return;
     691             :     }
     692             : 
     693             :     // Assign official branch pressure drop
     694       58500 :     if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).HasPressureComponents) {
     695       12870 :         BranchPressureDrop = TempBranchPressureDrop;
     696             :     }
     697             : 
     698             :     // Otherwise update the inlet node of the last component on the branch with this corrected pressure
     699             :     // This essentially sets all the pressure drop on the branch to be accounted for on the last component
     700       58500 :     state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(NumCompsOnBranch).NodeNumIn).Press =
     701       58500 :         state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(NumCompsOnBranch).NodeNumOut)
     702      117000 :             .Press +
     703       58500 :         BranchPressureDrop;
     704             : 
     705             :     // Then Smear any internal nodes with this new node pressure by working backward through
     706             :     // all but the last component, and passing node pressure upstream
     707       58500 :     if (NumCompsOnBranch > 1) {
     708           0 :         for (CompNum = NumCompsOnBranch - 1; CompNum >= 1; --CompNum) {
     709             : 
     710             :             // If this component is a pump, stop passing pressure upstream, and set flag to true for calling routine
     711           0 :             if (DataPlant::PlantEquipmentTypeIsPump[static_cast<int>(
     712           0 :                     state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(CompNum).Type)]) {
     713           0 :                 PumpFound = true;
     714           0 :                 break;
     715             :             }
     716             : 
     717             :             // Otherwise just pass pressure upstream and move on
     718           0 :             state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(CompNum).NodeNumIn).Press =
     719           0 :                 state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).Comp(CompNum).NodeNumOut).Press;
     720             :         }
     721             :     }
     722             : }
     723             : 
     724       21060 : void PassPressureAcrossMixer(
     725             :     EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation LoopSideNum, Real64 &MixerPressure, int const NumBranchesOnLoopSide)
     726             : {
     727             : 
     728             :     // SUBROUTINE INFORMATION:
     729             :     //       AUTHOR         Edwin Lee
     730             :     //       DATE WRITTEN   August 2009
     731             :     //       MODIFIED       na
     732             :     //       RE-ENGINEERED  na
     733             : 
     734             :     // PURPOSE OF THIS SUBROUTINE:
     735             :     // Set mixer inlet pressures, or in other words, set mixer inlet branch outlet pressures
     736             : 
     737             :     // METHODOLOGY EMPLOYED:
     738             :     // Set outlet node pressures for all parallel branches on this LoopSide
     739             :     // Note that this is extremely simple, but is set to it's own routine to allow for clarity
     740             :     //  when possible expansion occurs during further development
     741             : 
     742             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     743             :     int BranchNum;
     744             : 
     745       47970 :     for (BranchNum = 2; BranchNum <= NumBranchesOnLoopSide - 1; ++BranchNum) {
     746       26910 :         state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumOut).Press = MixerPressure;
     747             :     }
     748       21060 : }
     749             : 
     750       21060 : void PassPressureAcrossSplitter(EnergyPlusData &state,
     751             :                                 int const LoopNum,
     752             :                                 const DataPlant::LoopSideLocation LoopSideNum,
     753             :                                 Real64 &SplitterInletPressure)
     754             : {
     755             : 
     756             :     // SUBROUTINE INFORMATION:
     757             :     //       AUTHOR         Edwin Lee
     758             :     //       DATE WRITTEN   August 2009
     759             :     //       MODIFIED       na
     760             :     //       RE-ENGINEERED  na
     761             : 
     762             :     // PURPOSE OF THIS SUBROUTINE:
     763             :     // Set the splitter inlet pressure in anticipation of the inlet branch pressure being simulated
     764             : 
     765             :     // METHODOLOGY EMPLOYED:
     766             :     // Set outlet node of LoopSide inlet branch to splitter pressure
     767             :     // Note that this is extremely simple, but is set to it's own routine to allow for clarity
     768             :     //  when possible expansion occurs during further development
     769             : 
     770             :     // SUBROUTINE PARAMETER DEFINITIONS:
     771       21060 :     int constexpr InletBranchNum(1);
     772             : 
     773       21060 :     state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(InletBranchNum).NodeNumOut).Press =
     774       21060 :         SplitterInletPressure;
     775       21060 : }
     776             : 
     777             : //=================================================================================================!
     778             : 
     779       10530 : void PassPressureAcrossInterface(EnergyPlusData &state, int const LoopNum)
     780             : {
     781             : 
     782             :     // SUBROUTINE INFORMATION:
     783             :     //       AUTHOR         Edwin Lee
     784             :     //       DATE WRITTEN   August 2009
     785             :     //       MODIFIED       na
     786             :     //       RE-ENGINEERED  na
     787             : 
     788             :     // PURPOSE OF THIS SUBROUTINE:
     789             :     // Pass pressure backward across plant demand inlet/supply outlet interface
     790             : 
     791             :     // METHODOLOGY EMPLOYED:
     792             :     // Set outlet node pressure of supply side equal to inlet node pressure of demand side
     793             :     // Note that this is extremely simple, but is set to it's own routine to allow for clarity
     794             :     //  when possible expansion occurs during further development
     795             : 
     796             :     // Using/Aliasing
     797             : 
     798             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     799             :     int DemandInletNodeNum;
     800             :     int SupplyOutletNodeNum;
     801             : 
     802       10530 :     DemandInletNodeNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
     803       10530 :     SupplyOutletNodeNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
     804             : 
     805       10530 :     state.dataLoopNodes->Node(SupplyOutletNodeNum).Press = state.dataLoopNodes->Node(DemandInletNodeNum).Press;
     806       10530 : }
     807             : 
     808        4074 : Real64 ResolveLoopFlowVsPressure(EnergyPlusData &state,
     809             :                                  int const LoopNum,            // - Index of which plant/condenser loop is being simulated
     810             :                                  Real64 const SystemMassFlow,  // - Initial "guess" at system mass flow rate [kg/s]
     811             :                                  int const PumpCurveNum,       // - Pump curve to use when calling the curve manager for psi = f(phi)
     812             :                                  Real64 const PumpSpeed,       // - Pump rotational speed, [rps] (revs per second)
     813             :                                  Real64 const PumpImpellerDia, // - Nominal pump impeller diameter [m]
     814             :                                  Real64 const MinPhi,          // - Minimum allowable value of phi, requested by the pump manager from curve mgr
     815             :                                  Real64 const MaxPhi           // - Maximum allowable value of phi, requested by the pump manager from curve mgr
     816             : )
     817             : {
     818             : 
     819             :     // FUNCTION INFORMATION:
     820             :     //       AUTHOR         Kaustubh Phalak
     821             :     //       DATE WRITTEN   Feb 2010
     822             :     //       MODIFIED       na
     823             :     //       RE-ENGINEERED  na
     824             : 
     825             :     // PURPOSE OF THIS FUNCTION:
     826             :     // To provide a means to simulate a constant speed pump curve and system curve to
     827             :     //  find a more realistic operating point for the plant.
     828             : 
     829             :     // METHODOLOGY EMPLOYED:
     830             :     // Pressure drop of complete loop is found for a perticular flow rate.
     831             :     //  i.e. pressuredrop = K * massflow ^ 2
     832             :     // System curve is then solved with pump curve already entered
     833             :     //  and flow rate provided by the pump will be calculated.
     834             :     // This routine does not trap for errors if a pressure simulation is not to be performed.
     835             :     // Calling routine should only call this if needed.
     836             : 
     837             :     // Using/Aliasing
     838             :     using Curve::CurveValue;
     839             :     using FluidProperties::GetDensityGlycol;
     840             :     using FluidProperties::GetViscosityGlycol;
     841             : 
     842             :     // Return value
     843             :     Real64 ResolvedLoopMassFlowRate;
     844             : 
     845             :     // FUNCTION PARAMETER DEFINITIONS:
     846             :     static constexpr std::string_view RoutineName("ResolvedLoopMassFlowRate: ");
     847        4074 :     int constexpr MaxIters(100);
     848        4074 :     Real64 constexpr PressureConvergeCriteria(0.1); // Pa
     849        4074 :     Real64 constexpr ZeroTolerance(0.0001);
     850             : 
     851             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     852             :     Real64 PumpPressureRise;
     853             :     Real64 NodeTemperature;
     854             :     Real64 NodeDensity;
     855             :     Real64 SystemPressureDrop;
     856             :     Real64 PhiPump;
     857             :     Real64 PhiSystem;
     858             :     Real64 PsiPump;
     859             :     int FluidIndex;
     860             :     int Iteration;
     861             :     Real64 LocalSystemMassFlow;
     862             :     Real64 LoopEffectiveK;
     863             :     bool Converged;
     864        8148 :     Array1D<Real64> MassFlowIterativeHistory(3);
     865             :     Real64 MdotDeltaLatest;
     866             :     Real64 MdotDeltaPrevious;
     867             :     Real64 DampingFactor;
     868             : 
     869             :     // Get loop level data
     870        4074 :     FluidIndex = state.dataPlnt->PlantLoop(LoopNum).FluidIndex;
     871        4074 :     LoopEffectiveK = state.dataPlnt->PlantLoop(LoopNum).PressureEffectiveK;
     872        4074 :     SystemPressureDrop = LoopEffectiveK * pow_2(SystemMassFlow);
     873             : 
     874             :     // Read data off the node data structure
     875        4074 :     NodeTemperature = state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn).Temp;
     876        4074 :     NodeDensity = GetDensityGlycol(state, std::string(), NodeTemperature, FluidIndex, RoutineName);
     877             : 
     878             :     // Store the passed in (requested, design) flow to the local value for performing iterations
     879        4074 :     LocalSystemMassFlow = SystemMassFlow;
     880             : 
     881             :     // Check and warn if invalid condition exists
     882        4074 :     if (LoopEffectiveK <= ZeroTolerance) {
     883           0 :         ++state.dataPlantPressureSys->ZeroKWarningCounter;
     884           0 :         if (state.dataPlantPressureSys->ZeroKWarningCounter == 1) {
     885           0 :             ShowWarningError(state, "Pump pressure-flow resolution attempted, but invalid loop conditions encountered.");
     886           0 :             ShowContinueError(state, "Loop being calculated: " + state.dataPlnt->PlantLoop(LoopNum).Name);
     887           0 :             ShowContinueError(state, "An invalid pressure/flow condition existed which resulted in the approximation of");
     888           0 :             ShowContinueError(state, "the pressure coefficient K to be zero.  The pressure simulation will use the requested (design)");
     889           0 :             ShowContinueError(state, "pump flow in order to proceed with the simulation.  This warning is only issued once.");
     890             :         }
     891           0 :         ResolvedLoopMassFlowRate = SystemMassFlow;
     892           0 :         return ResolvedLoopMassFlowRate;
     893             :     }
     894             : 
     895             :     // Initialize flag
     896        4074 :     Converged = false;
     897             : 
     898             :     // Initialize the mass flow history array and damping factor
     899        4074 :     MassFlowIterativeHistory = LocalSystemMassFlow;
     900        4074 :     DampingFactor = 0.9;
     901             : 
     902             :     // Start Convergence Loop
     903       29682 :     for (Iteration = 1; Iteration <= MaxIters; ++Iteration) {
     904             : 
     905             :         // Calculate System Mass Flow Rate
     906       29682 :         LocalSystemMassFlow = std::sqrt(SystemPressureDrop / LoopEffectiveK);
     907             : 
     908       29682 :         MassFlowIterativeHistory = eoshift(MassFlowIterativeHistory, -1, LocalSystemMassFlow);
     909             : 
     910       29682 :         PhiSystem = LocalSystemMassFlow / (NodeDensity * PumpSpeed * PumpImpellerDia);
     911             : 
     912             :         // 4th order polynomial for non-dimensional pump curve
     913       29682 :         PhiPump = PhiSystem;
     914             : 
     915             :         // Constrain the value to the valid region
     916       29682 :         PhiPump = max(PhiPump, MinPhi);
     917       29682 :         PhiPump = min(PhiPump, MaxPhi);
     918             : 
     919             :         // Get the pump curve value from the curve manager
     920       29682 :         PsiPump = CurveValue(state, PumpCurveNum, PhiPump);
     921             : 
     922             :         // Calcuate Pump Pressure rise
     923       29682 :         PumpPressureRise = PsiPump * NodeDensity * pow_2(PumpSpeed) * pow_2(PumpImpellerDia);
     924             : 
     925             :         // Convergence Criteria Based on Pressure
     926       29682 :         if (std::abs(SystemPressureDrop - PumpPressureRise) < (PressureConvergeCriteria)) {
     927        4074 :             ResolvedLoopMassFlowRate = LocalSystemMassFlow;
     928        4074 :             Converged = true;
     929        4074 :             break;
     930             :         }
     931             : 
     932       25608 :         if (Iteration < 2) {
     933             :             // Don't do anything?
     934             :         } else {
     935       21534 :             MdotDeltaLatest = std::abs(MassFlowIterativeHistory(1) - MassFlowIterativeHistory(2));
     936       21534 :             MdotDeltaPrevious = std::abs(MassFlowIterativeHistory(2) - MassFlowIterativeHistory(3));
     937       21534 :             if (MdotDeltaLatest < MdotDeltaPrevious) {
     938             :                 // we are converging
     939             :                 // DampingFactor = MIN(DampingFactor * 1.1, 0.9d0)
     940             :             } else {
     941             :                 // we are stuck or diverging
     942        4074 :                 DampingFactor *= 0.9;
     943             :             }
     944             :         }
     945             : 
     946             :         // Update pressure value with damping factor
     947       25608 :         SystemPressureDrop = DampingFactor * PumpPressureRise + (1.0 - DampingFactor) * SystemPressureDrop;
     948             :     }
     949             : 
     950             :     // Check if we didn't converge
     951        4074 :     if (!Converged) {
     952           0 :         ++state.dataPlantPressureSys->MaxIterWarningCounter;
     953           0 :         if (state.dataPlantPressureSys->MaxIterWarningCounter == 1) {
     954           0 :             ShowWarningError(state, "Pump pressure-flow resolution attempted, but iteration loop did not converge.");
     955           0 :             ShowContinueError(state, "Loop being calculated: " + state.dataPlnt->PlantLoop(LoopNum).Name);
     956           0 :             ShowContinueError(state, "A mismatch between the pump curve entered and the pressure drop components");
     957           0 :             ShowContinueError(state, "on the loop may be the cause.  The pressure simulation will use the requested (design)");
     958           0 :             ShowContinueError(state, "pump flow in order to proceed with the simulation.  This warning is only issued once.");
     959             :         }
     960           0 :         ResolvedLoopMassFlowRate = SystemMassFlow;
     961           0 :         return ResolvedLoopMassFlowRate;
     962             :     }
     963             : 
     964        4074 :     return ResolvedLoopMassFlowRate;
     965             : }
     966             : 
     967             : //=================================================================================================!
     968             : 
     969        2313 : } // namespace EnergyPlus::PlantPressureSystem

Generated by: LCOV version 1.13