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