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