LCOV - code coverage report
Current view: top level - EnergyPlus - PlantPressureSystem.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 10.0 % 320 32
Test Date: 2025-05-22 16:09:37 Functions: 22.2 % 9 2

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

Generated by: LCOV version 2.0-1