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

Generated by: LCOV version 2.0-1