Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : #include <ObjexxFCL/member.functions.hh>
49 :
50 : #include <EnergyPlus/Data/EnergyPlusData.hh>
51 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
52 : #include <EnergyPlus/DataConvergParams.hh>
53 : #include <EnergyPlus/DataHVACGlobals.hh>
54 : #include <EnergyPlus/FluidProperties.hh>
55 : #include <EnergyPlus/General.hh>
56 : #include <EnergyPlus/HVACInterfaceManager.hh>
57 : #include <EnergyPlus/Plant/DataPlant.hh>
58 : #include <EnergyPlus/Plant/LoopSide.hh>
59 : #include <EnergyPlus/PlantCondLoopOperation.hh>
60 : #include <EnergyPlus/PlantPressureSystem.hh>
61 : #include <EnergyPlus/PlantUtilities.hh>
62 : #include <EnergyPlus/Pumps.hh>
63 : #include <EnergyPlus/UtilityRoutines.hh>
64 :
65 : namespace EnergyPlus {
66 : namespace DataPlant {
67 :
68 36489570 : void HalfLoopData::solve(EnergyPlusData &state, bool const FirstHVACIteration, bool &ReSimOtherSideNeeded)
69 : {
70 :
71 : // SUBROUTINE INFORMATION:
72 : // AUTHORS: Dan Fisher, Sankaranarayanan K P, Edwin Lee
73 : // DATE WRITTEN: April 1998
74 : // MODIFIED June 2005(Work in the Plant Super Manager Module)
75 : // July 2006
76 : // RE-ENGINEERED July 2010
77 :
78 : // PURPOSE OF THIS SUBROUTINE:
79 : // SimSupplyFlowSolution is the driver routine for plant loops. It performs
80 : // the following tasks for each half loop (supply or demand side):
81 : // 1. Calculate flow request for half loop
82 : // 2. Predict Loop Flow
83 : // 3. Simulate the inlet branch
84 : // 4. Simulate the parallel branches, distributing load if necessary
85 : // 5. Set flow rates on parallel branches
86 : // 6. Simulate outlet branch and update node and report variables
87 :
88 : // METHODOLOGY EMPLOYED:
89 : // The algorithm operates on a predictor/corrector flow setting method by simulating all available loop components
90 : // based on component requested flow rates, then enforcing continuity on all loop branch flows by calling
91 : // the flow resolver and locking those flows down. Available components are then re-simulated using the
92 : // corrected flow rates.
93 :
94 36489570 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
95 36489570 : int ThisSideInletNode = this->NodeNumIn;
96 :
97 36489570 : this->InitialDemandToLoopSetPoint = 0.0;
98 36489570 : this->CurrentAlterationsToDemand = 0.0;
99 36489570 : this->UpdatedDemandToLoopSetPoint = 0.0;
100 :
101 : // The following block is related to validating the flow control paths of the loop side
102 : // Since the control types are scheduled, I think BeginTimeStep should be a decent check frequency
103 36489570 : if (state.dataGlobal->BeginTimeStepFlag && this->OncePerTimeStepOperations) {
104 :
105 : // Initialize loop side controls -- could just be done for one loop since this routine inherently
106 : // loops over all plant/condenser loops. Not sure if the penalty is worth investigating.
107 12213644 : PlantCondLoopOperation::InitLoadDistribution(state, FirstHVACIteration);
108 :
109 : // Now that the op scheme types are updated, do LoopSide validation
110 12213644 : this->ValidateFlowControlPaths(state);
111 :
112 : // Set the flag to false so we won't do these again this time step
113 12213644 : this->OncePerTimeStepOperations = false;
114 :
115 : } else {
116 :
117 : // Set the flag to true so that it is activated for the next time step
118 24275926 : this->OncePerTimeStepOperations = true;
119 : }
120 :
121 : // Do pressure system initialize if this is the demand side (therefore once per whole loop)
122 36489570 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
123 18249734 : PlantPressureSystem::SimPressureDropSystem(state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Init);
124 : }
125 :
126 : // Turn on any previously disabled branches due to constant speed branch pump issue
127 36489570 : this->TurnOnAllLoopSideBranches();
128 :
129 36489570 : LoopSideLocation OtherLoopSide = static_cast<LoopSideLocation>(LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)]);
130 : // Do the actual simulation here every time
131 36489570 : this->DoFlowAndLoadSolutionPass(state, OtherLoopSide, ThisSideInletNode, FirstHVACIteration);
132 :
133 : // On constant speed branch pump loop sides we need to re-simulate
134 36489570 : if (this->hasConstSpeedBranchPumps) {
135 : // turn off any pumps connected to unloaded equipment and re-do the flow/load solution pass
136 76983 : this->DisableAnyBranchPumpsConnectedToUnloadedEquipment();
137 76983 : this->DoFlowAndLoadSolutionPass(state, OtherLoopSide, ThisSideInletNode, FirstHVACIteration);
138 : }
139 :
140 : // A couple things are specific to which LoopSide we are on // TODO: This whole block needs to be moved up to the loop level
141 36489570 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
142 :
143 : // Pass the loop information via the HVAC interface manager
144 54749202 : HVACInterfaceManager::UpdatePlantLoopInterface(state,
145 18249734 : this->plantLoc,
146 18249734 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut,
147 18249734 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn,
148 : ReSimOtherSideNeeded,
149 : thisPlantLoop.CommonPipeType);
150 :
151 : } else { // LoopSide == LoopSideLocation::Supply
152 :
153 : // Update pressure drop reporting, calculate total loop pressure drop for use elsewhere
154 18239836 : PlantPressureSystem::SimPressureDropSystem(state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Update);
155 :
156 : // Pass the loop information via the HVAC interface manager (only the flow)
157 54719508 : HVACInterfaceManager::UpdatePlantLoopInterface(state,
158 18239836 : this->plantLoc,
159 18239836 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut,
160 18239836 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn,
161 : ReSimOtherSideNeeded,
162 : thisPlantLoop.CommonPipeType);
163 :
164 : // Update the loop outlet node conditions
165 18239836 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
166 18239836 : .CheckLoopExitNode(state, FirstHVACIteration); // TODO: This is a loop level check, move out
167 :
168 18239836 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
169 18239836 : .UpdateLoopSideReportVars(state, this->InitialDemandToLoopSetPointSAVED, this->LoadToLoopSetPointThatWasntMet);
170 : }
171 36489570 : }
172 :
173 12213644 : void HalfLoopData::ValidateFlowControlPaths(EnergyPlusData &state)
174 : {
175 :
176 : // FUNCTION INFORMATION:
177 : // AUTHOR Edwin Lee
178 : // DATE WRITTEN July 2010
179 : // MODIFIED na
180 : // RE-ENGINEERED na
181 :
182 : // PURPOSE OF THIS FUNCTION:
183 : // This routine will scan all the loop side paths and validate the component topology according
184 : // to current topology rules and regulations.
185 :
186 : // METHODOLOGY EMPLOYED:
187 : // Scan this loop side and begin by scanning the first branch, then follow with the remainder of the flow paths
188 : // - this would be from splitter outlet nodes all the way to the loop side outlet node.
189 : // The current rules are that "other types" of components (as defined below in the references) can be placed along each
190 : // flow path as needed. At this point, any number of "load-range based" components can be placed along the flow
191 : // path. After this, the user is allowed to place another set of any number of "other types" of components.
192 : // The key restriction is that an "other type" of component may not be sandwiched by "load-range based" components.
193 : // This is due to the load range based needing to be simulated all at once along each flow path.
194 :
195 : // REFERENCES:
196 : // "other types" of components: basically not load-range based heat transfer components. This would include:
197 : // - demand based components such as coils
198 : // - component setpoint based operating components
199 : // - heat exchanger components including waterside economizers
200 : // "load-range based" components are heat transfer components which are controlled based on a single load range.
201 : // - currently only one load range based scheme is available at a given time, although other control types
202 : // may be enabled, such as component setpoint.
203 : // Pumps are separate components since the pump heat is not accounted for in the flow path order.
204 : // Improvements during the demand side rewrite has allowed pumps to be placed as -not- the first component on a branch
205 : // Pumps can be placed anywhere, even between load-range based components, since they will not affect loop load
206 :
207 : // RETURN VALUE:
208 : // Returns a control validator flow structure, including a flag for successful or not, then if not successful
209 : // the other values are filled in such as location on the loop where the error occurred and a message error description
210 :
211 : // FUNCTION PARAMETER DEFINITIONS:
212 12213644 : int constexpr Parallel(1);
213 12213644 : int constexpr Outlet(2);
214 :
215 : //~ Initialze
216 12213644 : bool EncounteredLRB = false;
217 12213644 : bool EncounteredNonLRBAfterLRB = false;
218 12213644 : int const NumParallelPaths = this->TotalBranches - 2;
219 :
220 : // We'll start by stepping through the first branch, which may be the only branch
221 : // If we find a load range based, then trip the flag and just keep moving
222 : // If we only have one branch and all is good, then RETURN early
223 : // If we have parallel branches, then start looping through each flow path to
224 : // decide if it is a valid path.
225 : // If any one path is invalid then all is wrong
226 12213644 : int firstBranchIndex = 1;
227 24629788 : for (int CompIndex = 1; CompIndex <= this->Branch(firstBranchIndex).TotalComponents; ++CompIndex) {
228 :
229 12416144 : auto const &this_component(this->Branch(firstBranchIndex).Comp(CompIndex));
230 :
231 : {
232 12416144 : switch (this_component.CurOpSchemeType) {
233 2856 : case OpScheme::HeatingRB:
234 : case OpScheme::CoolingRB: { //~ load range based
235 2856 : if (EncounteredNonLRBAfterLRB) {
236 : // We must have already encountered a LRB, then a non-LRB, and now another LRB, this is bad
237 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
238 0 : ShowContinueError(state, "PlaLoad range based components are separated by other control type components.");
239 0 : ShowContinueError(state, "Load Range Based should be grouped together on each flow path.");
240 0 : ShowFatalError(state, "Plant topology issue causes program termination");
241 : } else {
242 2856 : EncounteredLRB = true;
243 : }
244 2856 : break;
245 : }
246 6197258 : case DataPlant::OpScheme::Pump: { //~ pump
247 : // For now this is just a placeholder, because I think pumps will be available anywhere,
248 : // and they won't affect the load distribution
249 6197258 : break;
250 : }
251 6131175 : case DataPlant::OpScheme::NoControl: { //~ Such as pipes
252 : // For now this is just a placeholder, because these components shouldn't cause a problem anywhere...
253 6131175 : break;
254 : }
255 0 : case DataPlant::OpScheme::Invalid: { //~ Uninitialized, this should be a sufficient place to catch for this on branch 1
256 : // throw fatal
257 0 : ShowSevereError(state,
258 0 : "ValidateFlowControlPaths: Uninitialized operation scheme type for component Name: " + this_component.Name);
259 0 : ShowFatalError(state, "ValidateFlowControlPaths: developer notice, Inlet path validation loop");
260 0 : break;
261 : }
262 84855 : default: { //~ Other control type
263 84855 : if (EncounteredLRB) {
264 0 : EncounteredNonLRBAfterLRB = true;
265 : } else {
266 : // For now don't do anything, but we'll see...
267 : }
268 : }
269 : }
270 : }
271 : }
272 :
273 : // Return early if we only needed to do the one branch
274 12213644 : if (NumParallelPaths <= 0) {
275 70986 : return;
276 : }
277 :
278 : // Now, if we have multiple parallel branches, I think the easiest way is to go all the way from the inlet node
279 : // of each parallel branch to the loop outlet node and check the flow path
280 : // This way we don't have to remember the conditions on each of the parallel branches when we would finally move
281 : // to analyzing the outlet node when all done
282 : // This will reduce allocation on the heap because we will keep from storing that array
283 : // For each parallel path, we will need to check two branches: the parallel branch and the LoopSide outlet branch
284 67197678 : for (int PathCounter = 1; PathCounter <= NumParallelPaths; ++PathCounter) {
285 165165060 : for (int ParallelOrOutletIndex = Parallel; ParallelOrOutletIndex <= Outlet; ++ParallelOrOutletIndex) {
286 : int BranchIndex;
287 110110040 : if (ParallelOrOutletIndex == Parallel) {
288 : // The branch index will be the current pathtype + 1 to add the inlet branch
289 55055020 : BranchIndex = PathCounter + 1;
290 : } else { // ParallelOrOutletIndex == Outlet
291 : // The branch index will be the LoopSide outlet node
292 55055020 : BranchIndex = this->TotalBranches;
293 : }
294 :
295 : // Now that we have the branch index, let's do the control type check over all the components
296 220631771 : for (int CompIndex = 1; CompIndex <= this->Branch(BranchIndex).TotalComponents; ++CompIndex) {
297 :
298 110521731 : auto const &this_component(this->Branch(BranchIndex).Comp(CompIndex));
299 :
300 : {
301 110521731 : switch (this_component.CurOpSchemeType) {
302 6477363 : case OpScheme::HeatingRB:
303 : case OpScheme::CoolingRB: { //~ load range based
304 6477363 : if (EncounteredNonLRBAfterLRB) {
305 : // We must have already encountered a LRB, then a non-LRB, and now another LRB, this is bad
306 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
307 0 : ShowContinueError(state, "Load range based components are separated by other control type components.");
308 0 : ShowContinueError(state, "Load Range Based should be grouped together on each flow path.");
309 0 : ShowFatalError(state, "Plant topology issue causes program termination");
310 : } else {
311 6477363 : EncounteredLRB = true;
312 : }
313 6477363 : break;
314 : }
315 :
316 66789635 : case DataPlant::OpScheme::NoControl: { //~ Such as pipes
317 : // For now this is just a placeholder, because these components shouldn't cause a problem anywhere...
318 66789635 : break;
319 : }
320 81882 : case DataPlant::OpScheme::Pump: { //~ pump
321 : // For now this is just a placeholder, because I think pumps will be available anywhere,
322 : // and they won't affect the load distribution
323 81882 : break;
324 : }
325 0 : case DataPlant::OpScheme::Invalid: { //~ Uninitialized, this should be sufficient place to
326 : // catch for this on other branches
327 : // throw fatal error
328 0 : ShowSevereError(
329 0 : state, "ValidateFlowControlPaths: Uninitialized operation scheme type for component Name: " + this_component.Name);
330 0 : ShowFatalError(state, "ValidateFlowControlPaths: developer notice, problem in Parallel path validation loop");
331 0 : break;
332 : }
333 37172851 : default: { //~ Other control type
334 37172851 : if (EncounteredLRB) {
335 0 : EncounteredNonLRBAfterLRB = true;
336 : } else {
337 : // For now don't do anything, but we'll see...
338 : }
339 : }
340 : }
341 : }
342 :
343 : } //~ CompIndex
344 :
345 : } //~ Parallel and Outlet Branches
346 :
347 : } //~ Parallel Paths
348 : }
349 :
350 125746356 : bool HalfLoopData::CheckPlantConvergence(bool const FirstHVACIteration)
351 : {
352 :
353 : // FUNCTION INFORMATION:
354 : // AUTHOR Edwin Lee
355 : // DATE WRITTEN Summer 2011
356 : // MODIFIED na
357 : // RE-ENGINEERED na
358 :
359 : // PURPOSE OF THIS FUNCTION:
360 : // This routine checks the history values in the convergence arrays of this loop/LoopSide combination
361 :
362 : // METHODOLOGY EMPLOYED:
363 : // On FirstHVAC, we are not converged yet, thus forcing at least two iterations
364 : // Calculate the average of each related variable history (generalized: could be any number of history terms)
365 : // If any of the history terms do not match this average, then at least one value is different, so not converged
366 : // Although this routine appears to check for convergence, it is also used to check for stuck (max iteration) conditions
367 : // in cases where demand side (air loop, for example) equipment is "fighting" with the plant loop
368 : // The result of this routine can help the plant "lock-in" and take action to stop the iteration
369 :
370 : // Using/Aliasing
371 : using namespace DataPlant;
372 : using namespace DataLoopNode;
373 :
374 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
375 : Real64 InletAvgTemp;
376 : Real64 InletAvgMdot;
377 : Real64 OutletAvgTemp;
378 : Real64 OutletAvgMdot;
379 :
380 125746356 : if (FirstHVACIteration) {
381 56192889 : return false;
382 : }
383 :
384 69553467 : InletAvgTemp = sum(this->InletNode.TemperatureHistory) / size(this->InletNode.TemperatureHistory);
385 69553467 : if (any_ne(this->InletNode.TemperatureHistory, InletAvgTemp)) {
386 69048173 : return false;
387 : }
388 :
389 505294 : InletAvgMdot = sum(this->InletNode.MassFlowRateHistory) / size(this->InletNode.MassFlowRateHistory);
390 505294 : if (any_ne(this->InletNode.MassFlowRateHistory, InletAvgMdot)) {
391 219763 : return false;
392 : }
393 :
394 285531 : OutletAvgTemp = sum(this->OutletNode.TemperatureHistory) / size(this->OutletNode.TemperatureHistory);
395 285531 : if (any_ne(this->OutletNode.TemperatureHistory, OutletAvgTemp)) {
396 77931 : return false;
397 : }
398 :
399 207600 : OutletAvgMdot = sum(this->OutletNode.MassFlowRateHistory) / size(this->OutletNode.MassFlowRateHistory);
400 207600 : if (any_ne(this->OutletNode.MassFlowRateHistory, OutletAvgMdot)) {
401 6243 : return false;
402 : }
403 :
404 : // If we made it this far, we're good!
405 201357 : return true;
406 : }
407 :
408 125746356 : void HalfLoopData::PushBranchFlowCharacteristics(EnergyPlusData &state,
409 : int const BranchNum,
410 : Real64 const ValueToPush,
411 : bool const FirstHVACIteration // TRUE if First HVAC iteration of Time step
412 : )
413 : {
414 :
415 : // SUBROUTINE INFORMATION:
416 : // AUTHOR Edwin Lee
417 : // DATE WRITTEN September 2010
418 : // MODIFIED na
419 : // RE-ENGINEERED na
420 :
421 : // PURPOSE OF THIS SUBROUTINE:
422 : // This routine takes the flow resolved flow rate and pushes it
423 : // down a branch. In the process, if an externally connected
424 : // component (air-water coil for example) is found to have a
425 : // differing flow rate, the air sim flag is tripped to true, but
426 : // the flow resolved flow rate is pushed down the loop to allow
427 : // the plant to finish successfully.
428 :
429 : // METHODOLOGY EMPLOYED:
430 : // Push mass flow rate and max avail down each branch. If the component
431 : // is connected (or could be, for now) to an external loop such as
432 : // an air loop, the current component outlet mass flow is checked
433 : // vs the current resolved mass flow. If the mass flow doesn't match,
434 : // the air sim flag is tripped to true.
435 :
436 : // Currently this routine is only performed for starved branches, when
437 : // the coil is requesting too much flow, more than the plant can provide.
438 : // If this were moved to every call type, including a minimum plant flow,
439 : // you would need to provide a mass flow and min/max avail to push
440 : // down the branch as well.
441 :
442 : // Using/Aliasing
443 : using namespace DataPlant; // Use the entire module to allow all TypeOf's, would be a huge ONLY list
444 :
445 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
446 : int CompCounter;
447 : int BranchInletNode;
448 : DataPlant::PlantEquipmentType ComponentType;
449 : Real64 MassFlowRateFound;
450 : Real64 MassFlow;
451 : bool PlantIsRigid;
452 :
453 125746356 : auto &this_branch(this->Branch(BranchNum));
454 :
455 125746356 : BranchInletNode = this_branch.NodeNumIn;
456 :
457 : //~ Possible error handling if needed
458 125746356 : if (ValueToPush != state.dataLoopNodes->Node(BranchInletNode).MassFlowRate) {
459 : // Diagnostic problem, flow resolver isn't calling this routine properly
460 : }
461 :
462 : //~ This section would really be useful more later on if this routine has more logic regarding what to push down the branch
463 125746356 : MassFlow = ValueToPush;
464 : // MinAvail = ValueToPush
465 : // MaxAvail = ValueToPush
466 :
467 125746356 : PlantIsRigid = this->CheckPlantConvergence(FirstHVACIteration);
468 :
469 : //~ Loop across all component outlet nodes and update their mass flow and max avail
470 252146047 : for (CompCounter = 1; CompCounter <= this_branch.TotalComponents; ++CompCounter) {
471 :
472 126399691 : auto const &this_comp(this_branch.Comp(CompCounter));
473 :
474 : //~ Pick up some values for convenience
475 126399691 : int ComponentInletNode = this_comp.NodeNumIn;
476 126399691 : int ComponentOutletNode = this_comp.NodeNumOut;
477 126399691 : MassFlowRateFound = state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate;
478 126399691 : ComponentType = this_comp.Type;
479 :
480 : //~ Push the values through
481 126399691 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate = MassFlow;
482 :
483 126399691 : if (PlantIsRigid) {
484 202451 : state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMinAvail = MassFlow;
485 202451 : state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMaxAvail = MassFlow;
486 202451 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMinAvail = MassFlow;
487 202451 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMaxAvail = MassFlow;
488 : }
489 : // Node(ComponentOutletNode)%MassFlowRateMinAvail = MinAvail
490 : // no this is 2-way valve which messes up flow options
491 : // for demand components Node(ComponentOutletNode)%MassFlowRateMaxAvail = MaxAvail
492 :
493 : //~ If this value matches then we are good to move to the next component
494 126399691 : if (std::abs(MassFlow - MassFlowRateFound) < CriteriaDelta_MassFlowRate) {
495 113812740 : continue;
496 : }
497 : //~ Since there is a difference, we have to decide what to do based on the component type:
498 : //~ For plant connections, don't do anything, it SHOULD work itself out
499 : //~ For air connections, trip the LoopSide air flag
500 : //~ Similar for zone, none zone, and electric load center
501 : {
502 12586951 : switch (ComponentType) {
503 :
504 : // possibly air-connected components
505 918806 : case DataPlant::PlantEquipmentType::CoilWaterCooling:
506 : case DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling:
507 : case DataPlant::PlantEquipmentType::CoilWaterSimpleHeating:
508 : case DataPlant::PlantEquipmentType::CoilSteamAirHeating:
509 : case DataPlant::PlantEquipmentType::CoilWAHPHeatingEquationFit:
510 : case DataPlant::PlantEquipmentType::CoilWAHPCoolingEquationFit:
511 : case DataPlant::PlantEquipmentType::CoilWAHPHeatingParamEst:
512 : case DataPlant::PlantEquipmentType::CoilWAHPCoolingParamEst:
513 : case DataPlant::PlantEquipmentType::CoilUserDefined:
514 : case DataPlant::PlantEquipmentType::CoilVSWAHPCoolingEquationFit:
515 : case DataPlant::PlantEquipmentType::CoilVSWAHPHeatingEquationFit:
516 : case DataPlant::PlantEquipmentType::PackagedTESCoolingCoil: {
517 918806 : this->SimAirLoopsNeeded = true;
518 : // sometimes these coils are children in ZoneHVAC equipment
519 : // PlantLoop(LoopNum)%LoopSide(LoopSideNum)%SimZoneEquipNeeded= .TRUE.
520 918806 : break;
521 : }
522 :
523 : // zone connected components
524 24458 : case DataPlant::PlantEquipmentType::CoolingPanel_Simple:
525 : case DataPlant::PlantEquipmentType::Baseboard_Conv_Water:
526 : case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Steam:
527 : case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Water:
528 : case DataPlant::PlantEquipmentType::LowTempRadiant_VarFlow:
529 : case DataPlant::PlantEquipmentType::LowTempRadiant_ConstFlow:
530 : case DataPlant::PlantEquipmentType::CooledBeamAirTerminal:
531 : case DataPlant::PlantEquipmentType::ZoneHVACAirUserDefined:
532 : case DataPlant::PlantEquipmentType::AirTerminalUserDefined:
533 : case DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal: {
534 24458 : this->SimZoneEquipNeeded = true;
535 24458 : break;
536 : }
537 :
538 : // electric center connected components
539 5751 : case DataPlant::PlantEquipmentType::Generator_FCExhaust:
540 : case DataPlant::PlantEquipmentType::Generator_FCStackCooler:
541 : case DataPlant::PlantEquipmentType::Generator_MicroCHP:
542 : case DataPlant::PlantEquipmentType::Generator_MicroTurbine:
543 : case DataPlant::PlantEquipmentType::Generator_ICEngine:
544 : case DataPlant::PlantEquipmentType::Generator_CTurbine: {
545 5751 : this->SimElectLoadCentrNeeded = true;
546 5751 : break;
547 : }
548 :
549 11637936 : default:
550 11637936 : break;
551 : }
552 : }
553 : }
554 125746356 : }
555 :
556 36489570 : void HalfLoopData::TurnOnAllLoopSideBranches()
557 : {
558 208288389 : for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
559 171798819 : auto &branch = this->Branch(branchNum);
560 171798819 : branch.disableOverrideForCSBranchPumping = false;
561 : }
562 36489570 : }
563 :
564 73133106 : void HalfLoopData::SimulateAllLoopSideBranches(EnergyPlusData &state,
565 : Real64 const ThisLoopSideFlow,
566 : bool const FirstHVACIteration,
567 : bool &LoopShutDownFlag)
568 : {
569 :
570 : // SUBROUTINE INFORMATION:
571 : // AUTHOR Edwin Lee
572 : // DATE WRITTEN July 2010
573 : // MODIFIED na
574 : // RE-ENGINEERED na
575 :
576 : // PURPOSE OF THIS SUBROUTINE:
577 : // This routine will step through all branch groups (single branch .OR. inlet/parallels/outlet)
578 : // and call the branch group simulation routine. This routine also calls to update the splitter
579 : // and mixer.
580 :
581 : // METHODOLOGY EMPLOYED:
582 : // The number of branch groups is either 1 or 3. 1 would be a single branch half-loop. 3 would
583 : // be the minimum for an inlet/parallels/outlet set. The number of branch groups can then be
584 : // calculated as #BrGrps = 1 + 2*L; where L is zero for single half loop and one for parallel-type set.
585 : // This calculation can be reduced to the logical/integer conversion as shown in the code.
586 : // The simulation then steps through each branch group. If there are parallel branches, the splitter is
587 : // updated on flowlock=0 to pass information through, then after the parallel branches the mixer is always
588 : // updated. The outlet branch "group" is then simulated.
589 :
590 : // SUBROUTINE PARAMETER DEFINITIONS:
591 73133106 : int constexpr InletBranchOrOneBranchHalfLoop(1);
592 73133106 : int constexpr ParallelBranchSet(2);
593 73133106 : int constexpr OutletBranch(3);
594 :
595 73133106 : int NumBranchGroups = 1;
596 73133106 : if (this->TotalBranches > 1) {
597 72811510 : NumBranchGroups = 3;
598 : }
599 :
600 : // reset branch starting component index back to zero before each pass
601 563115334 : for (int BranchCounter = 1; BranchCounter <= this->TotalBranches; ++BranchCounter) {
602 489982228 : this->Branch(BranchCounter).lastComponentSimulated = 0;
603 : }
604 :
605 291889232 : for (int BranchGroup = 1; BranchGroup <= NumBranchGroups; ++BranchGroup) {
606 :
607 218756126 : if ((BranchGroup > 1) && (this->TotalBranches == 1)) {
608 0 : break;
609 : }
610 :
611 218756126 : switch (BranchGroup) {
612 73133106 : case InletBranchOrOneBranchHalfLoop:
613 73133106 : this->SimulateLoopSideBranchGroup(state, 1, 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
614 73133106 : break;
615 72811510 : case ParallelBranchSet:
616 72811510 : this->UpdatePlantSplitter(state);
617 72811510 : this->SimulateLoopSideBranchGroup(state, 2, this->TotalBranches - 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
618 72811510 : this->UpdatePlantMixer(state);
619 72811510 : break;
620 72811510 : case OutletBranch:
621 72811510 : this->SimulateLoopSideBranchGroup(
622 : state, this->TotalBranches, this->TotalBranches, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
623 72811510 : break;
624 : }
625 : }
626 73133106 : }
627 :
628 56466326 : void HalfLoopData::AdjustPumpFlowRequestByEMSControls(int const BranchNum, int const CompNum, Real64 &FlowToRequest)
629 : {
630 :
631 : // SUBROUTINE INFORMATION:
632 : // AUTHOR Brent Griffith
633 : // DATE WRITTEN April 2012
634 : // MODIFIED na
635 : // RE-ENGINEERED na
636 :
637 : // PURPOSE OF THIS SUBROUTINE:
638 : // modify flow request to pump simulation if EMS is overriding pump component
639 :
640 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
641 56466326 : auto &this_branch(this->Branch(BranchNum));
642 56466326 : auto &this_comp(this_branch.Comp(CompNum));
643 :
644 56466326 : if ((this->EMSCtrl) && (this->EMSValue <= 0.0)) {
645 0 : FlowToRequest = 0.0;
646 0 : return;
647 : }
648 :
649 56466326 : if ((this_branch.EMSCtrlOverrideOn) && (this_branch.EMSCtrlOverrideValue <= 0.0)) {
650 0 : FlowToRequest = 0.0;
651 0 : return;
652 : }
653 :
654 56466326 : if (this_comp.EMSLoadOverrideOn) {
655 296123 : if (this_comp.EMSLoadOverrideValue == 0.0) {
656 215627 : FlowToRequest = 0.0;
657 : }
658 : }
659 : }
660 :
661 76983 : void HalfLoopData::DisableAnyBranchPumpsConnectedToUnloadedEquipment()
662 : {
663 296970 : for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
664 219987 : auto &branch = this->Branch(branchNum);
665 219987 : Real64 totalDispatchedLoadOnBranch = 0.0;
666 593940 : for (int compNum = 1; compNum <= branch.TotalComponents; ++compNum) {
667 373953 : auto const &component = branch.Comp(compNum);
668 373953 : auto const &t = component.Type;
669 373953 : if (t == DataPlant::PlantEquipmentType::PumpConstantSpeed || t == DataPlant::PlantEquipmentType::PumpBankConstantSpeed ||
670 219987 : t == DataPlant::PlantEquipmentType::PumpVariableSpeed || t == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
671 : // don't do anything
672 : } else {
673 219987 : totalDispatchedLoadOnBranch += component.MyLoad;
674 : }
675 : }
676 219987 : if (std::abs(totalDispatchedLoadOnBranch) < 0.001) {
677 167100 : branch.disableOverrideForCSBranchPumping = true;
678 : }
679 : }
680 76983 : }
681 :
682 36566553 : Real64 HalfLoopData::EvaluateLoopSetPointLoad(EnergyPlusData &state, int const FirstBranchNum, int const LastBranchNum, Real64 ThisLoopSideFlow)
683 : {
684 :
685 : // FUNCTION INFORMATION:
686 : // AUTHOR Edwin Lee
687 : // DATE WRITTEN August 2010
688 : // MODIFIED na
689 : // RE-ENGINEERED na
690 :
691 : // Return value
692 36566553 : Real64 LoadToLoopSetPoint = 0.0; // function result
693 :
694 : static constexpr std::string_view RoutineName("PlantLoopSolver::EvaluateLoopSetPointLoad");
695 : static constexpr std::string_view RoutineNameAlt("PlantSupplySide:EvaluateLoopSetPointLoad");
696 :
697 : //~ General variables
698 36566553 : Real64 SumMdotTimesTemp = 0.0;
699 36566553 : Real64 SumMdot = 0.0;
700 :
701 36566553 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
702 :
703 : // We will place one specialized case in here for common pipe simulations.
704 : // If we are doing a common pipe simulation, and there is greater other-side flow than this side,
705 : // then the "other side" demand needs to include getting the flow through the common pipe to the same setpoint
706 : // as the flow going through the actual supply side
707 36566553 : if (this->hasConstSpeedBranchPumps && this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply &&
708 112046 : thisPlantLoop.CommonPipeType != DataPlant::CommonPipeType::No) {
709 19955 : const DataPlant::LoopSideLocation OtherSide = LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)];
710 19955 : const int otherSideOutletNodeNum = thisPlantLoop.LoopSide(OtherSide).NodeNumOut;
711 19955 : Real64 commonPipeFlow = state.dataLoopNodes->Node(otherSideOutletNodeNum).MassFlowRate - ThisLoopSideFlow;
712 19955 : Real64 otherSideExitingTemperature = state.dataLoopNodes->Node(otherSideOutletNodeNum).Temp;
713 19955 : SumMdotTimesTemp += otherSideExitingTemperature * commonPipeFlow;
714 19955 : SumMdot += commonPipeFlow;
715 : }
716 :
717 : // Sweep across flow paths in this group and calculate the deltaT and then the load
718 36566553 : int BranchIndex = 0; // ~ This is a 1 - n value within the current branch group
719 73133106 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
720 :
721 36566553 : ++BranchIndex;
722 :
723 : //~ Always start from the last component we did the last time around + 1 and
724 : //~ try to make it all the way to the end of the loop
725 36566553 : int StartingComponent = this->Branch(BranchCounter).lastComponentSimulated + 1;
726 36566553 : int EnteringNodeNum = this->Branch(BranchCounter).Comp(StartingComponent).NodeNumIn;
727 :
728 36566553 : Real64 EnteringTemperature = state.dataLoopNodes->Node(EnteringNodeNum).Temp;
729 36566553 : Real64 MassFlowRate = state.dataLoopNodes->Node(EnteringNodeNum).MassFlowRate;
730 :
731 36566553 : SumMdotTimesTemp += EnteringTemperature * MassFlowRate;
732 36566553 : SumMdot += MassFlowRate;
733 : }
734 :
735 36566553 : if (SumMdot < DataBranchAirLoopPlant::MassFlowTolerance) {
736 16995755 : return 0.0;
737 : }
738 :
739 19570798 : Real64 WeightedInletTemp = SumMdotTimesTemp / SumMdot;
740 :
741 19570798 : if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Water) {
742 :
743 19532712 : Real64 Cp = thisPlantLoop.glycol->getSpecificHeat(state, WeightedInletTemp, RoutineName);
744 :
745 : {
746 :
747 19532712 : if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
748 :
749 : // Pick up the loop setpoint temperature
750 19203492 : Real64 LoopSetPointTemperature = this->TempSetPoint;
751 : // Calculate the delta temperature
752 19203492 : Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
753 :
754 : // Calculate the demand on the loop
755 19203492 : LoadToLoopSetPoint = SumMdot * Cp * DeltaTemp;
756 :
757 329220 : } else if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
758 :
759 : // Get the range of setpoints
760 329220 : Real64 LoopSetPointTemperatureHi = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointHi;
761 329220 : Real64 LoopSetPointTemperatureLo = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointLo;
762 :
763 : // Calculate the demand on the loop
764 329220 : if (SumMdot > 0.0) {
765 329220 : Real64 LoadToHeatingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureLo - WeightedInletTemp);
766 329220 : Real64 LoadToCoolingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureHi - WeightedInletTemp);
767 : // Possible combinations:
768 : // 1 LoadToHeatingSetPoint > 0 & LoadToCoolingSetPoint > 0 --> Heating required
769 : // 2 LoadToHeatingSetPoint < 0 & LoadToCoolingSetPoint < 0 --> Cooling Required
770 : // 3 LoadToHeatingSetPoint <=0 & LoadToCoolingSetPoint >=0 --> Dead Band Operation - includes zero load cases
771 : // 4 LoadToHeatingSetPoint > LoadToCoolingSetPoint --> Not Feasible if LoopSetPointHi >= LoopSetPointLo
772 : // First trap bad set-points
773 329220 : if (LoadToHeatingSetPoint > LoadToCoolingSetPoint) {
774 0 : ShowSevereError(state,
775 : "Plant Loop: the Plant Loop Demand Calculation Scheme is set to DualSetPointDeadBand, but the "
776 : "heating-related low setpoint appears to be above the cooling-related high setpoint.");
777 0 : ShowContinueError(state,
778 : "For example, if using SetpointManager:Scheduled:DualSetpoint, then check that the low setpoint is "
779 : "below the high setpoint.");
780 0 : ShowContinueError(state, "Occurs in PlantLoop=" + thisPlantLoop.Name);
781 0 : ShowContinueError(
782 : state,
783 0 : format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
784 0 : ShowContinueError(state, format("Loop Heating Low Setpoint={:.2R}", LoopSetPointTemperatureLo));
785 0 : ShowContinueError(state, format("Loop Cooling High Setpoint={:.2R}", LoopSetPointTemperatureHi));
786 :
787 0 : ShowFatalError(state, "Program terminates due to above conditions.");
788 : }
789 329220 : if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
790 87679 : LoadToLoopSetPoint = LoadToHeatingSetPoint;
791 241541 : } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
792 140964 : LoadToLoopSetPoint = LoadToCoolingSetPoint;
793 100577 : } else if (LoadToHeatingSetPoint <= 0.0 && LoadToCoolingSetPoint >= 0.0) { // deadband includes zero loads
794 100577 : LoadToLoopSetPoint = 0.0;
795 : } else {
796 0 : ShowSevereError(state,
797 : "DualSetPointWithDeadBand: Unanticipated combination of heating and cooling loads - report to EnergyPlus "
798 : "Development Team");
799 0 : ShowContinueError(state, "occurs in PlantLoop=" + thisPlantLoop.Name);
800 0 : ShowContinueError(
801 : state,
802 0 : format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
803 0 : ShowContinueError(state, format("Loop Heating Setpoint={:.2R}", LoopSetPointTemperatureLo));
804 0 : ShowContinueError(state, format("Loop Cooling Setpoint={:.2R}", LoopSetPointTemperatureHi));
805 0 : ShowFatalError(state, "Program terminates due to above conditions.");
806 : }
807 : } else {
808 0 : LoadToLoopSetPoint = 0.0;
809 : }
810 : }
811 : }
812 :
813 38086 : } else if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Steam) {
814 :
815 38086 : Real64 Cp = thisPlantLoop.glycol->getSpecificHeat(state, WeightedInletTemp, RoutineName);
816 :
817 : {
818 :
819 38086 : if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
820 :
821 : // Pick up the loop setpoint temperature
822 38086 : Real64 LoopSetPointTemperature = this->TempSetPoint;
823 :
824 : // Calculate the delta temperature
825 38086 : Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
826 :
827 38086 : auto *steam = Fluid::GetSteam(state);
828 38086 : Real64 EnthalpySteamSatVapor = steam->getSatEnthalpy(state, LoopSetPointTemperature, 1.0, RoutineNameAlt);
829 38086 : Real64 EnthalpySteamSatLiquid = steam->getSatEnthalpy(state, LoopSetPointTemperature, 0.0, RoutineNameAlt);
830 :
831 38086 : Real64 LatentHeatSteam = EnthalpySteamSatVapor - EnthalpySteamSatLiquid;
832 :
833 : // Calculate the demand on the loop
834 38086 : LoadToLoopSetPoint = SumMdot * (Cp * DeltaTemp + LatentHeatSteam);
835 : }
836 : }
837 :
838 : } else { // only have two types, water serves for glycol.
839 : }
840 :
841 : // Trim the demand to zero if it is very small
842 19570798 : if (std::abs(LoadToLoopSetPoint) < DataPlant::LoopDemandTol) {
843 5065573 : LoadToLoopSetPoint = 0.0;
844 : }
845 :
846 19570798 : return LoadToLoopSetPoint;
847 : }
848 :
849 36566553 : Real64 HalfLoopData::CalcOtherSideDemand(EnergyPlusData &state, Real64 ThisLoopSideFlow)
850 : {
851 :
852 : // FUNCTION INFORMATION:
853 : // AUTHOR Edwin Lee
854 : // DATE WRITTEN August 2010
855 : // MODIFIED na
856 : // RE-ENGINEERED na
857 :
858 : // PURPOSE OF THIS FUNCTION:
859 : // To evaluate the demand to hit the loop setpoint based on the loop side inlet conditions
860 :
861 : // METHODOLOGY EMPLOYED:
862 : // This routine will simply call the evaluate loop setpoint routine but call it from
863 : // the very beginning of this loop side, so that it is basically for the entire loop side
864 :
865 : // FUNCTION PARAMETER DEFINITIONS:
866 36566553 : return this->EvaluateLoopSetPointLoad(state, 1, 1, ThisLoopSideFlow);
867 : }
868 :
869 36566553 : Real64 HalfLoopData::SetupLoopFlowRequest(EnergyPlusData &state, const LoopSideLocation OtherSide)
870 : {
871 :
872 : // FUNCTION INFORMATION:
873 : // AUTHOR: Dan Fisher, Edwin Lee
874 : // DATE WRITTEN: August 2010
875 : // MODIFIED: na
876 : // RE-ENGINEERED: na
877 :
878 : // PURPOSE OF THIS SUBROUTINE:
879 : // This routine sets up the flow request values and sums them up for each loop side
880 : // Then makes a decision on the desired loop flow based on loop configuration
881 :
882 : // METHODOLOGY EMPLOYED:
883 : // Scan through the components on this loop side, and look at the mass flow request
884 : // values on components inlet node.
885 : // Check common pipe/pumping configuration for this loop side and the other loop side
886 : // to determine what the LoopSide should flow
887 :
888 : //~ Initialize
889 36566553 : Real64 LoopFlow = 0.0; // Once all flow requests are evaluated, this is the desired flow on this side
890 :
891 : // reference
892 36566553 : auto &loop(state.dataPlnt->PlantLoop(this->plantLoc.loopNum));
893 :
894 : //~ First we need to set up the flow requests on each LoopSide
895 109699659 : for (DataPlant::LoopSideLocation LoopSideCounter : DataPlant::LoopSideKeys) {
896 : // Clear things out for this LoopSide
897 73133106 : Real64 InletBranchRequestNeedAndTurnOn = 0.0;
898 73133106 : Real64 InletBranchRequestNeedIfOn = 0.0;
899 73133106 : Real64 ParallelBranchRequestsNeedAndTurnOn(0.0);
900 73133106 : Real64 ParallelBranchRequestsNeedIfOn(0.0);
901 73133106 : Real64 OutletBranchRequestNeedAndTurnOn = 0.0;
902 73133106 : Real64 OutletBranchRequestNeedIfOn = 0.0;
903 :
904 : // reference
905 73133106 : auto &loop_side(loop.LoopSide(LoopSideCounter));
906 :
907 73133106 : loop_side.flowRequestNeedIfOn = 0.0;
908 73133106 : loop_side.flowRequestNeedAndTurnOn = 0.0;
909 73133106 : loop_side.flowRequestFinal = 0.0;
910 73133106 : loop_side.hasConstSpeedBranchPumps = false;
911 :
912 : // Now loop through all the branches on this LoopSide and get flow requests
913 73133106 : int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
914 73133106 : int ParallelBranchIndex = 0;
915 563005100 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
916 489871994 : Real64 ThisBranchFlowRequestNeedAndTurnOn = 0.0;
917 489871994 : Real64 ThisBranchFlowRequestNeedIfOn = 0.0;
918 :
919 : // reference
920 489871994 : auto &branch(loop_side.Branch(BranchCounter));
921 :
922 489871994 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) {
923 343930150 : ++ParallelBranchIndex;
924 : }
925 :
926 489871994 : if (branch.disableOverrideForCSBranchPumping) {
927 334188 : branch.RequestedMassFlow = 0.0;
928 334188 : continue;
929 : }
930 :
931 489537806 : int const NumCompsOnThisBranch = branch.TotalComponents;
932 982043472 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
933 :
934 : // reference
935 492505666 : auto &component(branch.Comp(CompCounter));
936 :
937 492505666 : int NodeToCheckRequest = component.NodeNumIn;
938 492505666 : LoopFlowStatus FlowPriorityStatus = component.FlowPriority;
939 :
940 : // reference
941 492505666 : auto const &node_with_request = state.dataLoopNodes->Node(NodeToCheckRequest);
942 :
943 492505666 : if (!DataPlant::PlantEquipmentTypeIsPump[static_cast<int>(component.Type)]) {
944 :
945 455197521 : if (FlowPriorityStatus == DataPlant::LoopFlowStatus::Invalid) {
946 : // do nothing
947 455182985 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyAndTurnsLoopOn) {
948 229785521 : ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, node_with_request.MassFlowRateRequest);
949 229785521 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
950 225397464 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyIfLoopOn) {
951 20800370 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
952 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::TakesWhatGets) {
953 : // do nothing
954 : }
955 : } else { // handle pumps differently
956 37308145 : if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
957 36189018 : (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay)) {
958 : // special primary side flow request for two way common pipe
959 409752 : int const CompIndex = component.CompNum;
960 409752 : switch (component.Type) {
961 : // remove var speed pumps from this case statement if can set MassFlowRateRequest
962 409752 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
963 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
964 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
965 409752 : if (CompIndex > 0) {
966 : ThisBranchFlowRequestNeedIfOn =
967 409752 : max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
968 : }
969 409752 : break;
970 0 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
971 0 : if (CompIndex > 0) {
972 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
973 0 : state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
974 0 : state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
975 : }
976 0 : break;
977 0 : default:
978 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
979 0 : break;
980 : }
981 :
982 37308145 : } else if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
983 35779266 : (loop.CommonPipeType == DataPlant::CommonPipeType::Single)) {
984 255612 : int const CompIndex = component.CompNum;
985 255612 : switch (component.Type) {
986 : // remove var speed pumps from this case statement if can set MassFlowRateRequest
987 255612 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
988 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
989 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed: {
990 255612 : if (CompIndex > 0) {
991 : ThisBranchFlowRequestNeedIfOn =
992 255612 : max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
993 : }
994 255612 : break;
995 : }
996 0 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
997 0 : if (CompIndex > 0) {
998 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
999 0 : state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
1000 0 : state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
1001 : }
1002 0 : break;
1003 0 : default:
1004 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
1005 : }
1006 255612 : } else {
1007 36642781 : int const CompIndex = component.CompNum;
1008 36642781 : switch (component.Type) {
1009 6401928 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
1010 6401928 : if (CompIndex > 0) {
1011 6401928 : auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
1012 6401928 : if (ParallelBranchIndex >= 1) { // branch pump
1013 259747 : if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1014 128603 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1015 131144 : } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
1016 18140 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1017 : }
1018 259747 : loop_side.hasConstSpeedBranchPumps = true;
1019 259747 : branch.HasConstantSpeedBranchPump = true;
1020 259747 : branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax;
1021 : } else { // inlet pump
1022 6142181 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1023 : }
1024 : }
1025 6401928 : break;
1026 14520 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
1027 14520 : if (CompIndex > 0) {
1028 14520 : auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
1029 14520 : if (ParallelBranchIndex >= 1) { // branch pump
1030 0 : if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1031 : ThisBranchFlowRequestNeedIfOn =
1032 0 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1033 0 : } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
1034 : ThisBranchFlowRequestNeedIfOn =
1035 0 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1036 : }
1037 0 : loop_side.hasConstSpeedBranchPumps = true;
1038 0 : branch.HasConstantSpeedBranchPump = true;
1039 0 : branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax / this_pump.NumPumpsInBank;
1040 : } else { // inlet pump
1041 : ThisBranchFlowRequestNeedIfOn =
1042 14520 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1043 : }
1044 : }
1045 14520 : break;
1046 :
1047 : // overwrite here for branch pumps
1048 30226333 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
1049 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
1050 : case DataPlant::PlantEquipmentType::PumpCondensate:
1051 30226333 : if (component.CompNum > 0) {
1052 30226333 : auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
1053 30226333 : this_pump.LoopSolverOverwriteFlag = false;
1054 : }
1055 : default:
1056 30226333 : break;
1057 : }
1058 : }
1059 : }
1060 : }
1061 489537806 : if (BranchCounter == 1) { // inlet branch
1062 73133106 : InletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
1063 73133106 : InletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
1064 416404700 : } else if (BranchCounter < NumBranchesOnThisLoopSide) { // branchcounter = 1 is already caught
1065 343595962 : ParallelBranchRequestsNeedAndTurnOn += ThisBranchFlowRequestNeedAndTurnOn;
1066 343595962 : ParallelBranchRequestsNeedIfOn += ThisBranchFlowRequestNeedIfOn;
1067 72808738 : } else if (BranchCounter == NumBranchesOnThisLoopSide) { // outlet branch
1068 72808738 : OutletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
1069 72808738 : OutletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
1070 : }
1071 :
1072 489537806 : branch.RequestedMassFlow = max(ThisBranchFlowRequestNeedIfOn, ThisBranchFlowRequestNeedAndTurnOn);
1073 : }
1074 73133106 : loop_side.flowRequestNeedAndTurnOn =
1075 73133106 : max(InletBranchRequestNeedAndTurnOn, ParallelBranchRequestsNeedAndTurnOn, OutletBranchRequestNeedAndTurnOn);
1076 73133106 : loop_side.flowRequestNeedIfOn = max(InletBranchRequestNeedIfOn, ParallelBranchRequestsNeedIfOn, OutletBranchRequestNeedIfOn);
1077 : }
1078 :
1079 36566553 : auto &this_loop_side(loop.LoopSide(this->plantLoc.loopSideNum));
1080 36566553 : auto &other_loop_side(loop.LoopSide(OtherSide));
1081 :
1082 : //~ Now that we have calculated each sides different status's requests, process to find final
1083 36566553 : if ((this_loop_side.flowRequestNeedAndTurnOn + other_loop_side.flowRequestNeedAndTurnOn) < DataBranchAirLoopPlant::MassFlowTolerance) {
1084 16977258 : this_loop_side.flowRequestFinal = 0.0;
1085 16977258 : other_loop_side.flowRequestFinal = 0.0;
1086 : } else { // some flow is needed and loop should try to run
1087 19589295 : this_loop_side.flowRequestFinal = max(this_loop_side.flowRequestNeedAndTurnOn, this_loop_side.flowRequestNeedIfOn);
1088 19589295 : other_loop_side.flowRequestFinal = max(other_loop_side.flowRequestNeedAndTurnOn, other_loop_side.flowRequestNeedIfOn);
1089 : }
1090 : // now store final flow requests on each loop side data structure
1091 36566553 : this_loop_side.FlowRequest = this_loop_side.flowRequestFinal;
1092 36566553 : other_loop_side.FlowRequest = other_loop_side.flowRequestFinal;
1093 :
1094 36566553 : if (loop.CommonPipeType == DataPlant::CommonPipeType::No) {
1095 : // we may or may not have a pump on this side, but the flow request is the larger of the two side's final
1096 35868303 : if ((!this_loop_side.hasConstSpeedBranchPumps) && (!other_loop_side.hasConstSpeedBranchPumps)) {
1097 35750142 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1098 : } else { // account for stepped loop flow rates required of branch pumps
1099 :
1100 : // rules for setting flow when there are constant speed branch pumps.
1101 : // 1. Check if above routines already selected a loop flow rate based on the constant speed branches, if so then just use it
1102 118161 : if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal >= other_loop_side.flowRequestFinal)) {
1103 : // okay, just use basic logic
1104 60288 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1105 57873 : } else if (other_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal <= other_loop_side.flowRequestFinal)) {
1106 : // okay, just use basic logic
1107 13204 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1108 : } else { // not okay, we have a case that will likely need special correcting
1109 : // 2. determine which loop side has the stepped data
1110 44669 : DataPlant::LoopSideLocation LoopSideIndex = DataPlant::LoopSideLocation::Invalid;
1111 44669 : if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal < other_loop_side.flowRequestFinal)) {
1112 31803 : LoopSideIndex = this->plantLoc.loopSideNum;
1113 12866 : } else if (other_loop_side.hasConstSpeedBranchPumps && (other_loop_side.flowRequestFinal < this_loop_side.flowRequestFinal)) {
1114 12866 : LoopSideIndex = OtherSide;
1115 : }
1116 44669 : auto &loop_side(loop.LoopSide(LoopSideIndex));
1117 :
1118 : // 3. step through and find out needed information
1119 : // 3a. search the loop side with branch pumps and find the steps available with non-zero Myloads
1120 : // 3b. search the loop side with branch pumps and find the steps available with zero Myloads
1121 : // LoadedConstantSpeedBranchFlowRateSteps = 0.0;
1122 44669 : Real64 LoadedConstantSpeedBranchFlowRateSteps_sum = 0.0;
1123 44669 : this_loop_side.noLoadConstantSpeedBranchFlowRateSteps = 0.0;
1124 44669 : Real64 NoLoadConstantSpeedBranchFlowRateSteps_sum = 0.0;
1125 44669 : int ParallelBranchIndex = 0;
1126 44669 : int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
1127 44669 : auto const &loop_branches(loop_side.Branch);
1128 268014 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
1129 223345 : auto const &loop_branch(loop_branches(BranchCounter));
1130 223345 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) {
1131 134007 : ++ParallelBranchIndex;
1132 : }
1133 223345 : if (loop_branch.HasConstantSpeedBranchPump) {
1134 89338 : Real64 const branch_mass_flow(loop_branch.ConstantSpeedBranchMassFlow);
1135 89338 : if (loop_branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1136 21007 : LoadedConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
1137 : } else {
1138 68331 : this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex) = branch_mass_flow;
1139 68331 : NoLoadConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
1140 : }
1141 : }
1142 : }
1143 :
1144 : // 4. allocate which branches to use,
1145 44669 : Real64 tmpLoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1146 44669 : Real64 MaxBranchPumpLoopSideFlow = LoadedConstantSpeedBranchFlowRateSteps_sum + NoLoadConstantSpeedBranchFlowRateSteps_sum;
1147 44669 : tmpLoopFlow = min(tmpLoopFlow, MaxBranchPumpLoopSideFlow);
1148 : // 4b. first use all the branches with non-zero MyLoad
1149 44669 : if (tmpLoopFlow > LoadedConstantSpeedBranchFlowRateSteps_sum) {
1150 44669 : Real64 AccumFlowSteps = LoadedConstantSpeedBranchFlowRateSteps_sum;
1151 44669 : ParallelBranchIndex = 0;
1152 134007 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
1153 134007 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) {
1154 89338 : ++ParallelBranchIndex;
1155 : } else {
1156 44669 : continue;
1157 : }
1158 89338 : Real64 const steps = this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex);
1159 89338 : if (steps > 0.0) { // add in branches with zero MyLoad in branch input order until satisfied
1160 68331 : if (tmpLoopFlow > AccumFlowSteps) {
1161 68331 : if (tmpLoopFlow <= AccumFlowSteps + steps) { // found it set requests and exit
1162 44669 : tmpLoopFlow = AccumFlowSteps + steps;
1163 44669 : loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
1164 44669 : LoopFlow = tmpLoopFlow;
1165 44669 : break;
1166 : } else {
1167 23662 : AccumFlowSteps += steps;
1168 23662 : loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
1169 : }
1170 : }
1171 : }
1172 : }
1173 : }
1174 : }
1175 : }
1176 698250 : } else if (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
1177 434322 : LoopFlow = this_loop_side.flowRequestFinal;
1178 263928 : } else if (loop.CommonPipeType == DataPlant::CommonPipeType::Single) {
1179 263928 : LoopFlow = this_loop_side.flowRequestFinal;
1180 : }
1181 :
1182 : // overrides the loop solver flow request to allow loop pump to turn off when not in use
1183 36566553 : if (this_loop_side.TotalPumps == 1) {
1184 18499225 : if (LoopFlow < HVAC::VerySmallMassFlow) { // Update from dataconvergetols...
1185 44285474 : for (int BranchCounter = 1; BranchCounter <= this_loop_side.TotalBranches; ++BranchCounter) {
1186 : // reference
1187 35884097 : auto &branch(this_loop_side.Branch(BranchCounter));
1188 35884097 : int const NumCompsOnThisBranch = branch.TotalComponents;
1189 72237692 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
1190 36353595 : auto const &component(branch.Comp(CompCounter));
1191 36353595 : switch (component.Type) {
1192 7202304 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
1193 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
1194 : case DataPlant::PlantEquipmentType::PumpCondensate:
1195 7202304 : if (component.CompNum > 0) {
1196 7202304 : auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
1197 7202304 : this_pump.LoopSolverOverwriteFlag = true;
1198 : }
1199 : default:
1200 36353595 : break;
1201 : }
1202 : }
1203 : }
1204 : }
1205 : }
1206 :
1207 36566553 : return LoopFlow;
1208 : }
1209 :
1210 36566553 : void HalfLoopData::DoFlowAndLoadSolutionPass(EnergyPlusData &state, LoopSideLocation OtherSide, int ThisSideInletNode, bool FirstHVACIteration)
1211 : {
1212 :
1213 : // This is passed in-out deep down into the depths where the load op manager calls EMS and EMS can shut down pumps
1214 36566553 : bool LoopShutDownFlag = false;
1215 :
1216 : // First thing is to setup mass flow request information
1217 36566553 : Real64 ThisLoopSideFlowRequest = this->SetupLoopFlowRequest(state, OtherSide);
1218 :
1219 : // Now we know what the loop would "like" to run at, let's see the pump
1220 : // operation range (min/max avail) to see whether it is possible this time around
1221 36566553 : Real64 ThisLoopSideFlow = this->DetermineLoopSideFlowRate(state, ThisSideInletNode, ThisLoopSideFlowRequest);
1222 :
1223 281557667 : for (auto &branch : this->Branch) {
1224 244991114 : branch.lastComponentSimulated = 0;
1225 : }
1226 :
1227 : // We also need to establish a baseline "other-side-based" loop demand based on this possible flow rate
1228 36566553 : this->InitialDemandToLoopSetPoint = this->CalcOtherSideDemand(state, ThisLoopSideFlow);
1229 36566553 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
1230 36566553 : this->LoadToLoopSetPointThatWasntMet = 0.0;
1231 :
1232 : // We now have a loop side flow request, along with inlet min/max avails.
1233 : // We can now make a first pass through the component simulation, requesting flow as necessary.
1234 : // Normal "supply side" components will set a mass flow rate on their outlet node to request flow,
1235 : // while "Demand side" components will set a a mass flow request on their inlet node to request flow.
1236 36566553 : this->FlowLock = DataPlant::FlowLock::Unlocked;
1237 36566553 : this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
1238 :
1239 : // discussion/comments about loop solver/flow resolver interaction
1240 : // At this point, the components have been simulated. They should have either:
1241 : // - logged a massflowrequest
1242 : // - or logged a MassFlowRate
1243 : // We need to decide what the components are going to do on FlowLock=0.
1244 : // If we want all control here at the solver level, the components just need to
1245 : // log their MassFlowRate on their outlet nodes, or some other mechanism.
1246 : // Then the loop solver can scan the branch and get the max, and this will be the requested
1247 : // flow rate for the branch.
1248 : // The loop solver will then set this as the branch outlet mass flow rate in preparation
1249 : // for the flow resolver.
1250 : // The loop solver may need to do something to the inlet/outlet branch, but I'm not sure yet.
1251 : // The following comment block is what I had already thought of, and it may still make sense.
1252 :
1253 : // Now that all the flow requests have been logged, we need to prepare them for the
1254 : // flow resolver. This will just take the requests and determine the desired flow
1255 : // request for that branch according to pump placement, pump type, and other component
1256 : // conditions. In many cases, this will just be to simply take the max request from
1257 : // the branch, which will already be within pumping limits for that flow path.
1258 : // We can then call the flow resolver to lock down branch inlet flow rates.
1259 :
1260 : // The flow resolver takes information such as requested flows and min/max available flows and
1261 : // sets the corrected flow on the inlet to each parallel branch
1262 36566553 : this->ResolveParallelFlows(state, ThisLoopSideFlow, FirstHVACIteration);
1263 :
1264 : // Re-Initialize variables for this next pass
1265 36566553 : this->InitialDemandToLoopSetPointSAVED = this->InitialDemandToLoopSetPoint;
1266 36566553 : this->CurrentAlterationsToDemand = 0.0;
1267 36566553 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
1268 :
1269 : // Now that flow rates have been resolved, we just need to set the flow lock status
1270 : // flag, and resimulate. During this simulation each component will still use the
1271 : // SetFlowRequest routine, but this routine will also set the outlet flow rate
1272 : // equal to the inlet flow rate, according to flowlock logic.
1273 36566553 : this->FlowLock = DataPlant::FlowLock::Locked;
1274 36566553 : this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
1275 36566553 : }
1276 :
1277 36566553 : void HalfLoopData::ResolveParallelFlows(EnergyPlusData &state,
1278 : Real64 const ThisLoopSideFlow, // [kg/s] total flow to be split
1279 : bool const FirstHVACIteration // TRUE if First HVAC iteration of Time step
1280 : )
1281 : {
1282 :
1283 : // SUBROUTINE INFORMATION:
1284 : // AUTHOR Brandon Anderson, Dan Fisher
1285 : // DATE WRITTEN October 1999
1286 : // MODIFIED May 2005 Sankaranarayanan K P, Rich Liesen
1287 : // RE-ENGINEERED Sept 2010 Dan Fisher, Brent Griffith for demand side update
1288 :
1289 : // PURPOSE OF THIS SUBROUTINE:
1290 : // This subroutine takes the overall loop side flow and distributes
1291 : // it among parallel branches. this is the main implementation of
1292 : // flow splitting for plant splitter/mixer
1293 :
1294 : // METHODOLOGY EMPLOYED:
1295 : // Flow through the branches is currently determined by
1296 : // the active component on the branch, as well as the
1297 : // order of the branches following the splitter.
1298 : // SimPlantEquipment is run first, and the active components
1299 : // request their flow. These flows are compared and a simple
1300 : // algorithm balances flow in the branches. The flow in these
1301 : // branches is then locked down, via MassFlowRateMaxAvail and MinAvail
1302 : // SimPlant Equipment is then run again in order to get correct
1303 : // properties. Finally, Max/MinAvail are reset for the next time step.
1304 :
1305 : // Using/Aliasing
1306 :
1307 : // SUBROUTINE PARAMETER DEFINITIONS:
1308 36566553 : int constexpr LoopSideSingleBranch(1); // For readability
1309 :
1310 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1311 : Real64 ActiveFlowRate; // The flow available when cycling through branches
1312 : Real64 PassiveFlowRate; // The flow available when cycling through branches
1313 : Real64 FracFlow; // The flow available when cycling through branches
1314 : Real64 ThisBranchRequestFrac; // The request ratio
1315 : Real64 totalMax; // The flow available when cycling through branches
1316 : Real64 FlowRemaining; // The flow available when cycling through branches
1317 : int LastNodeOnBranch; // intermediate value used for better readabilty
1318 : int FirstNodeOnBranch; // intermediate value used for better readabilty
1319 : Real64 BranchFlowReq;
1320 : Real64 BranchMinAvail;
1321 : Real64 BranchMaxAvail;
1322 : Real64 ParallelBranchMaxAvail;
1323 : Real64 ParallelBranchMinAvail;
1324 : Real64 TotParallelBranchFlowReq;
1325 : Real64 StartingFlowRate;
1326 : Real64 ThisBranchRequest;
1327 :
1328 : // If there is no splitter then there is no continuity to enforce.
1329 36566553 : if (!this->Splitter.Exists) {
1330 :
1331 : // If there's only one branch, then RETURN
1332 160798 : if (this->TotalBranches == 1) {
1333 : // The branch should just try to meet the request previously calculated. This should be good,
1334 : // just need to make sure that during FlowUnlocked, no one constrained Min/Max farther.
1335 : // This would have been propagated down the branch, so we can check the outlet node min/max avail for this.
1336 160798 : auto const &this_single_branch(this->Branch(LoopSideSingleBranch));
1337 160798 : LastNodeOnBranch = this_single_branch.NodeNumOut;
1338 160798 : FirstNodeOnBranch = this_single_branch.NodeNumIn;
1339 160798 : BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
1340 160798 : BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
1341 160798 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = min(max(ThisLoopSideFlow, BranchMinAvail), BranchMaxAvail);
1342 : // now with flow locked, this single branch will just ran at the specified flow rate, so we are done
1343 31976615 : return;
1344 : } else {
1345 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
1346 0 : ShowContinueError(state, "There are multiple branches, yet no splitter. This is an invalid configuration.");
1347 0 : ShowContinueError(state, "Add a set of connectors, use put components on a single branch.");
1348 0 : ShowFatalError(state, "Invalid plant topology causes program termination.");
1349 0 : return;
1350 : }
1351 : }
1352 :
1353 : // If a splitter/mixer combination exist on the loop
1354 36405755 : if (this->Splitter.Exists && this->Mixer.Exists) {
1355 :
1356 : // Zero out local variables
1357 36405755 : TotParallelBranchFlowReq = 0.0;
1358 36405755 : int NumSplitOutlets = this->Splitter.TotalOutletNodes;
1359 36405755 : if (NumSplitOutlets < 1) {
1360 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
1361 0 : ShowContinueError(state, "Diagnostic error in PlantLoopSolver::ResolveParallelFlows.");
1362 0 : ShowContinueError(state, "Splitter improperly specified, no splitter outlets.");
1363 0 : ShowFatalError(state, "Invalid plant topology causes program termination.");
1364 : }
1365 :
1366 36405755 : int NumActiveBranches = 0;
1367 36405755 : ParallelBranchMaxAvail = 0.0;
1368 36405755 : ParallelBranchMinAvail = 0.0;
1369 208424561 : for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
1370 :
1371 172018806 : int BranchNum = this->Splitter.BranchNumOut(iBranch);
1372 172018806 : auto &this_branch(this->Branch(BranchNum));
1373 172018806 : int SplitterBranchOut = this->Splitter.BranchNumOut(iBranch);
1374 172018806 : auto const &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
1375 172018806 : LastNodeOnBranch = this_branch.NodeNumOut;
1376 172018806 : FirstNodeOnBranch = this_branch.NodeNumIn;
1377 172018806 : BranchFlowReq = this_branch.DetermineBranchFlowRequest(state);
1378 172018806 : this_branch.RequestedMassFlow = BranchFlowReq; // store this for later use in logic for remaining flow allocations
1379 : // now, if we are have branch pumps, here is the situation:
1380 : // constant speed pumps lock in a flow request on the inlet node
1381 : // variable speed pumps which have other components on the branch do not log a request themselves
1382 : // the DetermineBranchFlowRequest routine only looks at the branch inlet node
1383 : // for variable speed branch pumps then, this won't work because the branch will be requesting zero
1384 : // so let's adjust for this here to make sure these branches get good representation
1385 : // This comment above is not true, for series active branches, DetermineBranchFlowRequest does scan down the branch's
1386 : // components already, no need to loop over components
1387 172018806 : BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
1388 172018806 : BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
1389 : // !sum the branch flow requests to a total parallel branch flow request
1390 172018806 : bool activeBranch = this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active;
1391 172018806 : bool isSeriesActiveAndRequesting =
1392 172018806 : (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) && (BranchFlowReq > 0.0);
1393 172018806 : if (activeBranch || isSeriesActiveAndRequesting) { // revised logic for series active
1394 136256183 : TotParallelBranchFlowReq += BranchFlowReq;
1395 136256183 : ++NumActiveBranches;
1396 : }
1397 172018806 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = BranchFlowReq;
1398 172018806 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail = BranchMinAvail;
1399 172018806 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail = BranchMaxAvail;
1400 172018806 : ParallelBranchMaxAvail += BranchMaxAvail;
1401 172018806 : ParallelBranchMinAvail += BranchMinAvail;
1402 : }
1403 : // ! Find branch number and flow rates at splitter inlet
1404 36405755 : int SplitterBranchIn = this->Splitter.BranchNumIn;
1405 36405755 : int FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
1406 : // ! Find branch number and flow rates at mixer outlet
1407 36405755 : int MixerBranchOut = this->Mixer.BranchNumOut;
1408 36405755 : LastNodeOnBranch = this->Branch(MixerBranchOut).NodeNumOut;
1409 36405755 : int FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1410 :
1411 36405755 : auto &first_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchIn));
1412 36405755 : auto &last_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchOut));
1413 :
1414 : // Reset branch inlet node flow rates for the first and last branch on loop
1415 36405755 : first_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
1416 36405755 : last_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
1417 :
1418 : // Reset branch inlet node Min/MaxAvails for the first and last branch on loop
1419 36405755 : first_branch_inlet_node.MassFlowRateMaxAvail = min(first_branch_inlet_node.MassFlowRateMaxAvail, ParallelBranchMaxAvail);
1420 36405755 : first_branch_inlet_node.MassFlowRateMaxAvail =
1421 36405755 : min(first_branch_inlet_node.MassFlowRateMaxAvail, last_branch_inlet_node.MassFlowRateMaxAvail);
1422 36405755 : first_branch_inlet_node.MassFlowRateMinAvail = max(first_branch_inlet_node.MassFlowRateMinAvail, ParallelBranchMinAvail);
1423 36405755 : first_branch_inlet_node.MassFlowRateMinAvail =
1424 36405755 : max(first_branch_inlet_node.MassFlowRateMinAvail, last_branch_inlet_node.MassFlowRateMinAvail);
1425 36405755 : last_branch_inlet_node.MassFlowRateMinAvail = first_branch_inlet_node.MassFlowRateMinAvail;
1426 36405755 : last_branch_inlet_node.MassFlowRateMaxAvail = first_branch_inlet_node.MassFlowRateMaxAvail;
1427 :
1428 : // Initialize the remaining flow variable
1429 36405755 : FlowRemaining = ThisLoopSideFlow;
1430 :
1431 : // Initialize flow on passive, bypass and uncontrolled parallel branches to zero. For these branches
1432 : // MinAvail is not enforced
1433 208424561 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1434 172018806 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1435 172018806 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1436 208195337 : if (this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::Active &&
1437 36176531 : this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::SeriesActive) {
1438 35273341 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
1439 70546682 : this->PushBranchFlowCharacteristics(
1440 35273341 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1441 : }
1442 : }
1443 :
1444 : // IF SUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
1445 36405755 : if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) { // no flow available at all for splitter
1446 88985894 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1447 72047933 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1448 144476366 : for (int CompCounter = 1; CompCounter <= this->Branch(SplitterBranchOut).TotalComponents; ++CompCounter) {
1449 :
1450 72428433 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1451 72428433 : int CompInletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumIn;
1452 72428433 : int CompOutletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumOut;
1453 72428433 : state.dataLoopNodes->Node(CompInletNode).MassFlowRate = 0.0;
1454 72428433 : state.dataLoopNodes->Node(CompInletNode).MassFlowRateMaxAvail = 0.0;
1455 72428433 : state.dataLoopNodes->Node(CompOutletNode).MassFlowRate = 0.0;
1456 72428433 : state.dataLoopNodes->Node(CompOutletNode).MassFlowRateMaxAvail = 0.0;
1457 : }
1458 : }
1459 16937961 : return;
1460 19467794 : } else if (FlowRemaining >= TotParallelBranchFlowReq) {
1461 :
1462 : // 1) Satisfy flow demand of ACTIVE splitter outlet branches
1463 95134228 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1464 80256372 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1465 80256372 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1466 95056171 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
1467 14799799 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
1468 : // branch flow is min of requested flow and remaining flow
1469 65852718 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1470 65852718 : min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FlowRemaining);
1471 65852718 : if (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) {
1472 17890488 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
1473 : }
1474 131705436 : this->PushBranchFlowCharacteristics(
1475 65852718 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1476 65852718 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1477 65852718 : if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) {
1478 13996306 : FlowRemaining = 0.0;
1479 : }
1480 : }
1481 : }
1482 : // IF the active branches take the entire loop flow, return
1483 14877856 : if (FlowRemaining == 0.0) {
1484 10450512 : return;
1485 : }
1486 :
1487 : // 2) Distribute remaining flow to PASSIVE branches
1488 4427344 : totalMax = 0.0;
1489 31367676 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1490 26940332 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1491 26940332 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1492 26940332 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
1493 : // Calculate the total max available
1494 0 : totalMax += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
1495 : }
1496 : }
1497 :
1498 4427344 : if (totalMax > 0) {
1499 0 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1500 0 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1501 0 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1502 0 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
1503 0 : FracFlow = FlowRemaining / totalMax;
1504 0 : if (FracFlow <= 1.0) { // the passive branches will take all the flow
1505 0 : PassiveFlowRate = FracFlow * state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
1506 : // Check against FlowRemaining
1507 0 : PassiveFlowRate = min(FlowRemaining, PassiveFlowRate);
1508 : // Allow FlowRequest to be increased to meet minimum on branch
1509 0 : PassiveFlowRate = max(PassiveFlowRate, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail);
1510 0 : FlowRemaining = max((FlowRemaining - PassiveFlowRate), 0.0);
1511 0 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = PassiveFlowRate;
1512 : } else { // Each Branch receives maximum flow and BYPASS must be used
1513 0 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1514 0 : min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail, FlowRemaining);
1515 0 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1516 : }
1517 0 : this->PushBranchFlowCharacteristics(
1518 0 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1519 : }
1520 : }
1521 : } // totalMax <=0 and flow should be assigned to active branches
1522 : // IF the passive branches take the remaining loop flow, return
1523 4427344 : if (FlowRemaining == 0.0) {
1524 0 : return;
1525 : }
1526 :
1527 : // 3) Distribute remaining flow to the BYPASS
1528 31367676 : for (int OutletNum = 1; OutletNum <= this->Splitter.TotalOutletNodes; ++OutletNum) {
1529 26940332 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1530 26940332 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1531 26940332 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Bypass) {
1532 4194383 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1533 4194383 : min(FlowRemaining, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
1534 8388766 : this->PushBranchFlowCharacteristics(
1535 4194383 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1536 4194383 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1537 : }
1538 : }
1539 : // IF the bypass take the remaining loop flow, return
1540 4427344 : if (FlowRemaining == 0.0) {
1541 4186764 : return;
1542 : }
1543 :
1544 : // 4) If PASSIVE branches and BYPASS are at max and there's still flow, distribute remaining flow to ACTIVE branches but only those
1545 : // that had a non-zero flow request. Try to leave branches off that wanted to be off.
1546 240580 : if (NumActiveBranches > 0) {
1547 193866 : ActiveFlowRate = FlowRemaining / NumActiveBranches; // denominator now only includes active branches that wanted to be "on"
1548 282409 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1549 265331 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1550 265331 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1551 265331 : bool branchIsActive = this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active;
1552 : bool branchIsSeriesActiveAndRequesting =
1553 283406 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive &&
1554 18075 : this->Branch(SplitterBranchOut).RequestedMassFlow > 0.0;
1555 265331 : if (branchIsActive || branchIsSeriesActiveAndRequesting) { // only series active branches that want to be "on"
1556 : // check Remaining flow (should be correct!)
1557 263140 : ActiveFlowRate = min(ActiveFlowRate, FlowRemaining);
1558 : // set the flow rate to the MIN((MassFlowRate+AvtiveFlowRate), MaxAvail)
1559 263140 : StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1560 263140 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1561 263140 : min((state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate + ActiveFlowRate),
1562 263140 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
1563 526280 : this->PushBranchFlowCharacteristics(
1564 263140 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1565 : // adjust the remaining flow
1566 263140 : FlowRemaining -= (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate - StartingFlowRate);
1567 : }
1568 265331 : if (FlowRemaining == 0) {
1569 176788 : break;
1570 : }
1571 : }
1572 : // IF the active branches take the remaining loop flow, return
1573 193866 : if (FlowRemaining == 0.0) {
1574 176788 : return;
1575 : }
1576 :
1577 : // 5) Step 4) could have left ACTIVE branches < MaxAvail. Check to makes sure all ACTIVE branches are at MaxAvail
1578 62084 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1579 45006 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1580 45006 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1581 49767 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
1582 4761 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
1583 43717 : StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1584 : ActiveFlowRate =
1585 43717 : min(FlowRemaining, (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail - StartingFlowRate));
1586 43717 : FlowRemaining -= ActiveFlowRate;
1587 43717 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = StartingFlowRate + ActiveFlowRate;
1588 87434 : this->PushBranchFlowCharacteristics(
1589 43717 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1590 : }
1591 : }
1592 : }
1593 : // IF the active branches take the remaining loop flow, return
1594 63792 : if (FlowRemaining == 0.0) {
1595 7979 : return;
1596 : }
1597 :
1598 : // 6) Adjust Inlet branch and outlet branch flow rates to match parallel branch rate
1599 55813 : TotParallelBranchFlowReq = 0.0;
1600 123717 : for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
1601 67904 : int BranchNum = this->Splitter.BranchNumOut(iBranch);
1602 67904 : FirstNodeOnBranch = this->Branch(BranchNum).NodeNumIn;
1603 : // calculate parallel branch flow rate
1604 67904 : TotParallelBranchFlowReq += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1605 : }
1606 : // Reset the flow on the splitter inlet branch
1607 55813 : SplitterBranchIn = this->Splitter.BranchNumIn;
1608 55813 : FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
1609 55813 : state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate = TotParallelBranchFlowReq;
1610 111626 : this->PushBranchFlowCharacteristics(
1611 55813 : state, SplitterBranchIn, state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate, FirstHVACIteration);
1612 : // Reset the flow on the Mixer outlet branch
1613 55813 : MixerBranchOut = this->Mixer.BranchNumOut;
1614 55813 : FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1615 55813 : state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
1616 111626 : this->PushBranchFlowCharacteristics(
1617 55813 : state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
1618 55813 : return;
1619 :
1620 : // IF INSUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
1621 : } else {
1622 :
1623 : // 1) apportion flow based on requested fraction of total
1624 24304439 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1625 :
1626 19714501 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1627 19714501 : ThisBranchRequest = this->Branch(SplitterBranchOut).DetermineBranchFlowRequest(state);
1628 19714501 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1629 19714501 : auto &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
1630 :
1631 19714501 : if ((this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active) ||
1632 4456821 : (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive)) {
1633 :
1634 : // since we are calculating this fraction based on the total parallel request calculated above, we must mimic the logic to
1635 : // make sure the math works every time that means we must make the variable speed pump correction here as well.
1636 31011629 : for (int CompCounter = 1; CompCounter <= this_splitter_outlet_branch.TotalComponents; ++CompCounter) {
1637 :
1638 15594136 : auto const &this_comp(this_splitter_outlet_branch.Comp(CompCounter));
1639 :
1640 : // if this isn't a variable speed pump then just keep cycling
1641 15594136 : if ((this_comp.Type != PlantEquipmentType::PumpVariableSpeed) &&
1642 15592494 : (this_comp.Type != PlantEquipmentType::PumpBankVariableSpeed)) {
1643 15592494 : continue;
1644 : }
1645 :
1646 1642 : int CompInletNode = this_comp.NodeNumIn;
1647 1642 : ThisBranchRequest = max(ThisBranchRequest, state.dataLoopNodes->Node(CompInletNode).MassFlowRateRequest);
1648 : }
1649 :
1650 15417493 : ThisBranchRequestFrac = ThisBranchRequest / TotParallelBranchFlowReq;
1651 : // FracFlow = state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate/TotParallelBranchFlowReq
1652 : // state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate = MIN((FracFlow *
1653 : // state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate),FlowRemaining)
1654 15417493 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = ThisBranchRequestFrac * ThisLoopSideFlow;
1655 30834986 : this->PushBranchFlowCharacteristics(
1656 15417493 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1657 15417493 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1658 : }
1659 : }
1660 :
1661 : // 1b) check if flow all apportioned
1662 4589938 : if (FlowRemaining > DataBranchAirLoopPlant::MassFlowTolerance) {
1663 : // Call fatal diagnostic error. !The math should work out!
1664 0 : ShowSevereError(state, "ResolveParallelFlows: Dev note, failed to redistribute restricted flow");
1665 0 : ShowContinueErrorTimeStamp(state, "");
1666 0 : ShowContinueError(state, format("Loop side flow = {:.8R} (kg/s)", ThisLoopSideFlow));
1667 0 : ShowContinueError(state, format("Flow Remaining = {:.8R} (kg/s)", FlowRemaining));
1668 0 : ShowContinueError(state, format("Parallel Branch requests = {:.8R} (kg/s)", TotParallelBranchFlowReq));
1669 : }
1670 :
1671 : // 2) ! Reset the flow on the Mixer outlet branch
1672 4589938 : MixerBranchOut = this->Mixer.BranchNumOut;
1673 4589938 : FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1674 4589938 : state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
1675 9179876 : this->PushBranchFlowCharacteristics(
1676 4589938 : state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
1677 :
1678 : } // Total flow requested >= or < Total parallel request
1679 :
1680 : } // Splitter/Mixer exists
1681 : }
1682 :
1683 218756126 : void HalfLoopData::SimulateLoopSideBranchGroup(EnergyPlusData &state,
1684 : int const FirstBranchNum,
1685 : int const LastBranchNum,
1686 : Real64 FlowRequest,
1687 : bool const FirstHVACIteration,
1688 : bool &LoopShutDownFlag)
1689 : {
1690 :
1691 : // SUBROUTINE INFORMATION:
1692 : // AUTHOR Edwin Lee
1693 : // DATE WRITTEN July 2010
1694 : // MODIFIED na
1695 : // RE-ENGINEERED na
1696 :
1697 : // PURPOSE OF THIS SUBROUTINE:
1698 : // This routine will manage the component simulation on a single set of parallel branches
1699 : // This routine also reverts to a single branch simulation if there isn't a set of parallel branches
1700 :
1701 : // METHODOLOGY EMPLOYED:
1702 : // Loop through all components, and simulate first the non-load range based on each branch.
1703 : // When a load-range based (LRB) is encountered, the simulation moves to the next branch to do non-LRB components.
1704 : // When all paths are exhausted the simulation begins simulating LRB components. Before each comp, the load distribution
1705 : // engine is called to handle the load distribution for this current pass. If load is successfully distributed, this is
1706 : // flagged, and not called again. If load is not distributed (i.e. this component isn't ON right now), then the
1707 : // load distribution engine will be called again before the next component.
1708 : // After all load distribution is done and those components are complete, the simulation moves back to do any
1709 : // remaining components that may be downstream.
1710 :
1711 : //~ Flags
1712 : bool LoadDistributionWasPerformed;
1713 :
1714 : //~ General variables
1715 : Real64 LoadToLoopSetPoint;
1716 218756126 : PlantLocation PumpLocation;
1717 218756126 : LoadToLoopSetPoint = 0.0;
1718 :
1719 : // We now know what plant simulation region is available to us, let's simulate this group
1720 218756126 : bool EncounteredLRBObjDuringPass1(false);
1721 708738354 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1722 489982228 : auto &branch(this->Branch(BranchCounter));
1723 :
1724 : //~ Always start from the last component we did the last time around + 1 and
1725 : //~ try to make it all the way to the end of the loop
1726 489982228 : int const StartingComponent = branch.lastComponentSimulated + 1;
1727 489982228 : int const EndingComponent = branch.TotalComponents;
1728 943606336 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1729 :
1730 492479658 : auto &this_comp(branch.Comp(CompCounter));
1731 492479658 : PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
1732 492479658 : DataPlant::OpScheme const CurOpSchemeType(this_comp.CurOpSchemeType);
1733 :
1734 492479658 : switch (CurOpSchemeType) {
1735 0 : case DataPlant::OpScheme::WSEcon: //~ coils
1736 0 : this_comp.MyLoad = UpdatedDemandToLoopSetPoint;
1737 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1738 0 : break;
1739 37643402 : case DataPlant::OpScheme::Pump: //~ pump
1740 37643402 : if (this->BranchPumpsExist) {
1741 644952 : SimulateSinglePump(state, this_comp.location, branch.RequestedMassFlow);
1742 : } else {
1743 36998450 : SimulateSinglePump(state, this_comp.location, FlowRequest);
1744 : }
1745 37643402 : break;
1746 1161430 : case DataPlant::OpScheme::CompSetPtBased:
1747 1161430 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1748 : this_plantLoc,
1749 : LoadToLoopSetPoint,
1750 1161430 : LoadToLoopSetPointThatWasntMet,
1751 : FirstHVACIteration,
1752 : LoopShutDownFlag,
1753 : LoadDistributionWasPerformed);
1754 1161430 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1755 1161430 : break;
1756 322816 : case DataPlant::OpScheme::EMS:
1757 322816 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
1758 322816 : int const curCompOpSchemePtr = this_comp.CurCompLevelOpNum;
1759 322816 : int const OpSchemePtr = this_comp.OpScheme(curCompOpSchemePtr).OpSchemePtr;
1760 322816 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).OpScheme(OpSchemePtr).EMSIntVarLoopDemandRate = InitialDemandToLoopSetPoint;
1761 : }
1762 322816 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1763 : this_plantLoc,
1764 : UpdatedDemandToLoopSetPoint,
1765 322816 : LoadToLoopSetPointThatWasntMet,
1766 : FirstHVACIteration,
1767 : LoopShutDownFlag,
1768 : LoadDistributionWasPerformed);
1769 322816 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1770 322816 : break;
1771 38855550 : case OpScheme::WetBulbRB:
1772 : case OpScheme::DryBulbRB:
1773 : case OpScheme::DewPointRB:
1774 : case OpScheme::RelHumRB:
1775 : case OpScheme::DryBulbTDB:
1776 : case OpScheme::WetBulbTDB:
1777 : case OpScheme::DewPointTDB:
1778 : case OpScheme::HeatingRB:
1779 : case OpScheme::CoolingRB: { //~ load range based
1780 38855550 : EncounteredLRBObjDuringPass1 = true;
1781 38855550 : goto components_end; // don't do any more components on this branch
1782 : }
1783 414496460 : default: // demand, etc.
1784 414496460 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1785 : }
1786 :
1787 : // Update loop demand as needed for changes this component may have made
1788 453624108 : this->UpdateAnyLoopDemandAlterations(state, BranchCounter, CompCounter);
1789 :
1790 : //~ If we didn't EXIT early, we must have simulated, so update array
1791 453624108 : branch.lastComponentSimulated = CompCounter;
1792 :
1793 : } //~ CompCounter
1794 451126678 : components_end:;
1795 :
1796 489982228 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1797 244991114 : PlantPressureSystem::SimPressureDropSystem(
1798 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1799 : }
1800 :
1801 : } //~ BranchCounter
1802 :
1803 : // So now we have made one pass through all of the available components on these branches, skipping load based
1804 : // If we didn't encounter any load based objects during the first pass, then we must be done!
1805 218756126 : if (!EncounteredLRBObjDuringPass1) {
1806 218756126 : return;
1807 : }
1808 :
1809 : // If we have load based now, we should go ahead and distribute the load
1810 : // If not then this branch group is done, since flow path validation was previously done
1811 34242036 : LoadToLoopSetPoint = UpdatedDemandToLoopSetPoint;
1812 34242036 : LoadDistributionWasPerformed = false;
1813 :
1814 : // The way the load distribution is set up, I think I should call this for every load range based component
1815 : // encountered until distribution is actually performed. If we don't call for each component then we may
1816 : // call for a component that is not on the current equip list and then nothing would come on.
1817 34242036 : bool EncounteredNonLBObjDuringPass2(false);
1818 109079008 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1819 74836972 : auto &branch(this->Branch(BranchCounter));
1820 :
1821 : //~ Always start from the last component we did the last time around + 1 and
1822 : //~ try to make it all the way to the end of the loop
1823 74836972 : int const StartingComponent = branch.lastComponentSimulated + 1;
1824 74836972 : int const EndingComponent = branch.TotalComponents;
1825 114514178 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1826 39677206 : PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
1827 :
1828 39677206 : DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
1829 :
1830 39677206 : switch (CurOpSchemeType) {
1831 109656 : case DataPlant::OpScheme::NoControl: //~ pipes, for example
1832 109656 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1833 109656 : break;
1834 0 : case DataPlant::OpScheme::Demand:
1835 : case DataPlant::OpScheme::CompSetPtBased:
1836 : case DataPlant::OpScheme::FreeRejection: //~ other control types
1837 0 : EncounteredNonLBObjDuringPass2 = true;
1838 0 : goto components2_end; // don't do anymore components on this branch
1839 0 : case DataPlant::OpScheme::Pump: //~ pump
1840 0 : PumpLocation.loopNum = this->plantLoc.loopNum;
1841 0 : PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
1842 0 : PumpLocation.branchNum = BranchCounter;
1843 0 : PumpLocation.compNum = CompCounter;
1844 0 : if (this->BranchPumpsExist) {
1845 0 : SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
1846 : } else {
1847 0 : SimulateSinglePump(state, PumpLocation, FlowRequest);
1848 : }
1849 0 : break;
1850 39567550 : case OpScheme::WetBulbRB:
1851 : case OpScheme::DryBulbRB:
1852 : case OpScheme::DewPointRB:
1853 : case OpScheme::RelHumRB:
1854 : case OpScheme::DryBulbTDB:
1855 : case OpScheme::WetBulbTDB:
1856 : case OpScheme::DewPointTDB:
1857 : case OpScheme::HeatingRB:
1858 : case OpScheme::CoolingRB: { //~ load range based
1859 39567550 : if (!LoadDistributionWasPerformed) { //~ Still need to distribute load among load range based components
1860 37429386 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1861 : this_plantLoc,
1862 : LoadToLoopSetPoint,
1863 37429386 : LoadToLoopSetPointThatWasntMet,
1864 : FirstHVACIteration,
1865 : LoopShutDownFlag,
1866 : LoadDistributionWasPerformed);
1867 : }
1868 39567550 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1869 39567550 : break;
1870 : }
1871 0 : default:
1872 0 : break;
1873 : }
1874 :
1875 : //~ If we didn't EXIT early, we must have simulated, so update array
1876 39677206 : branch.lastComponentSimulated = CompCounter;
1877 :
1878 : } //~ CompCounter
1879 74836972 : components2_end:;
1880 :
1881 : //~ If we are locked, go ahead and simulate the pressure components on this branch
1882 74836972 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1883 37418486 : PlantPressureSystem::SimPressureDropSystem(
1884 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1885 : }
1886 :
1887 : } //~ BranchCounter
1888 :
1889 : // So now we have made the load range based pass through all the components on each branch
1890 : // If we didn't see any other component types, then we are done, go away
1891 34242036 : if (!EncounteredNonLBObjDuringPass2) {
1892 34242036 : return;
1893 : }
1894 :
1895 : // If we did encounter other objects than we just need to go back through and simulate them
1896 0 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1897 0 : auto &branch(this->Branch(BranchCounter));
1898 :
1899 : //~ Always start from the last component we did the last time around + 1 and
1900 : //~ try to make it all the way to the end of the loop
1901 0 : int const StartingComponent = branch.lastComponentSimulated + 1;
1902 0 : int const EndingComponent = branch.TotalComponents;
1903 0 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1904 :
1905 0 : DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
1906 :
1907 0 : switch (CurOpSchemeType) {
1908 0 : case DataPlant::OpScheme::Demand: //~ coils
1909 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1910 0 : break;
1911 0 : case DataPlant::OpScheme::Pump: //~ pump
1912 0 : PumpLocation.loopNum = this->plantLoc.loopNum;
1913 0 : PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
1914 0 : PumpLocation.branchNum = BranchCounter;
1915 0 : PumpLocation.compNum = CompCounter;
1916 0 : if (this->BranchPumpsExist) {
1917 0 : SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
1918 : } else {
1919 0 : SimulateSinglePump(state, PumpLocation, FlowRequest);
1920 : }
1921 0 : break;
1922 0 : case OpScheme::HeatingRB:
1923 : case OpScheme::CoolingRB: { //~ load range based
1924 0 : ShowFatalError(state, "Encountered Load Based Object after other components, invalid.");
1925 0 : break;
1926 : }
1927 0 : default:
1928 : //~ Typical control equipment
1929 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1930 : }
1931 :
1932 : //~ If we didn't EXIT early, we must have simulated, so update array
1933 0 : branch.lastComponentSimulated = CompCounter;
1934 :
1935 : } //~ CompCounter
1936 :
1937 0 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1938 0 : PlantPressureSystem::SimPressureDropSystem(
1939 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1940 : }
1941 :
1942 : } //~ BranchCounter
1943 :
1944 : // I suppose I could do a check on the last component simulated to make sure we actually exhausted all branches
1945 : // This would be the "THIRD" check on flow validation, but would be OK
1946 : }
1947 :
1948 453624108 : void HalfLoopData::UpdateAnyLoopDemandAlterations(EnergyPlusData &state, int const BranchNum, int const CompNum)
1949 : {
1950 :
1951 : // SUBROUTINE INFORMATION:
1952 : // AUTHOR Edwin Lee
1953 : // DATE WRITTEN August 2010
1954 : // MODIFIED na
1955 : // RE-ENGINEERED na
1956 :
1957 : // PURPOSE OF THIS SUBROUTINE:
1958 : // This routine will analyze the given component and determine if any
1959 : // alterations need to be made to the current loop demand value. If so,
1960 : // it will make the changes to the module level loop demand variables.
1961 :
1962 : // METHODOLOGY EMPLOYED:
1963 : // Components will always supply a useful delta T, even if it happens to be zero
1964 : // For flow rate, make decisions based on the component's current operating scheme type:
1965 : // Demand based: these components will have a flow request on their inlet node
1966 : // Pump: these components will not be included, as they no longer include heat at the pump
1967 : // component setpoint: these components will have a flow request
1968 :
1969 : // on their outlet node corresponding to their calculated delta T
1970 : // load range based: these components do not 'alter' the load, they reject the load
1971 : // Therefore they are not included
1972 :
1973 : // SUBROUTINE PARAMETER DEFINITIONS:
1974 : static constexpr std::string_view RoutineName("PlantLoopSolver::UpdateAnyLoopDemandAlterations");
1975 :
1976 : // Init to zero, so that if we don't find anything, we exit early
1977 453624108 : Real64 ComponentMassFlowRate(0.0);
1978 :
1979 453624108 : auto const &this_comp(this->Branch(BranchNum).Comp(CompNum));
1980 :
1981 : // Get information
1982 453624108 : int const InletNode(this_comp.NodeNumIn);
1983 453624108 : int const OutletNode(this_comp.NodeNumOut);
1984 :
1985 453624108 : if (this->FlowLock == DataPlant::FlowLock::Unlocked) {
1986 :
1987 226812054 : switch (this_comp.CurOpSchemeType) {
1988 0 : case OpScheme::HeatingRB:
1989 : case OpScheme::CoolingRB: { //~ load range based
1990 0 : break; // Don't do anything for load based components
1991 : }
1992 :
1993 226812054 : default: {
1994 : // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
1995 226812054 : ComponentMassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
1996 : // make sure components like economizers use the mass flow request
1997 226812054 : break;
1998 : }
1999 : }
2000 :
2001 226812054 : } else if (this->FlowLock == DataPlant::FlowLock::Locked) {
2002 :
2003 : // For locked flow just use the mass flow rate
2004 :
2005 226812054 : switch (this_comp.CurOpSchemeType) {
2006 0 : case OpScheme::HeatingRB:
2007 : case OpScheme::CoolingRB: { //~ load range based
2008 0 : break; // Don't do anything for load based components
2009 : }
2010 226812054 : default: {
2011 : // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
2012 226812054 : ComponentMassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
2013 : }
2014 : }
2015 :
2016 : } else { // flow pump query? problem?
2017 : }
2018 :
2019 : // Leave early if there wasn't a mass flow rate or request
2020 453624108 : if (ComponentMassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) {
2021 292663930 : return;
2022 : }
2023 :
2024 : // Get an average temperature for the property call
2025 160960178 : Real64 const InletTemp(state.dataLoopNodes->Node(InletNode).Temp);
2026 160960178 : Real64 const OutletTemp(state.dataLoopNodes->Node(OutletNode).Temp);
2027 160960178 : Real64 const AverageTemp((InletTemp + OutletTemp) / 2.0);
2028 160960178 : Real64 const ComponentCp(state.dataPlnt->PlantLoop(this->plantLoc.loopNum).glycol->getSpecificHeat(state, AverageTemp, RoutineName));
2029 :
2030 : // Calculate the load altered by this component
2031 160960178 : Real64 const LoadAlteration(ComponentMassFlowRate * ComponentCp * (OutletTemp - InletTemp));
2032 :
2033 : // Now alter the module level variables
2034 160960178 : this->CurrentAlterationsToDemand += LoadAlteration;
2035 160960178 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint - this->CurrentAlterationsToDemand;
2036 : }
2037 :
2038 37643402 : void HalfLoopData::SimulateSinglePump(EnergyPlusData &state, PlantLocation const SpecificPumpLocation, Real64 &SpecificPumpFlowRate)
2039 : {
2040 :
2041 : // SUBROUTINE INFORMATION:
2042 : // AUTHOR Edwin Lee
2043 : // DATE WRITTEN July 2010
2044 : // MODIFIED na
2045 : // RE-ENGINEERED na
2046 :
2047 37643402 : auto &loop(state.dataPlnt->PlantLoop(SpecificPumpLocation.loopNum));
2048 37643402 : auto &loop_side(loop.LoopSide(SpecificPumpLocation.loopSideNum));
2049 37643402 : auto &loop_side_branch(loop_side.Branch(SpecificPumpLocation.branchNum));
2050 37643402 : auto &comp(loop_side_branch.Comp(SpecificPumpLocation.compNum));
2051 37643402 : int const PumpIndex = comp.IndexInLoopSidePumps;
2052 37643402 : auto &pump(loop_side.Pumps(PumpIndex));
2053 :
2054 37643402 : this->AdjustPumpFlowRequestByEMSControls(SpecificPumpLocation.branchNum, SpecificPumpLocation.compNum, SpecificPumpFlowRate);
2055 :
2056 : // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
2057 : bool DummyThisPumpRunning;
2058 37643402 : Pumps::SimPumps(state,
2059 37643402 : pump.PumpName,
2060 37643402 : SpecificPumpLocation.loopNum,
2061 : SpecificPumpFlowRate,
2062 : DummyThisPumpRunning,
2063 37643402 : loop_side_branch.PumpIndex,
2064 37643402 : pump.PumpHeatToFluid);
2065 :
2066 : //~ Pull some state information from the pump outlet node
2067 37643402 : pump.CurrentMinAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMinAvail;
2068 37643402 : pump.CurrentMaxAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMaxAvail;
2069 :
2070 : //~ Update the LoopSide pump heat totality here
2071 37643402 : if (loop_side.TotalPumps > 0) {
2072 37643402 : loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
2073 : }
2074 37643402 : }
2075 :
2076 18662865 : void HalfLoopData::SimulateAllLoopSidePumps(EnergyPlusData &state,
2077 : ObjexxFCL::Optional<PlantLocation const> SpecificPumpLocation,
2078 : ObjexxFCL::Optional<Real64 const> SpecificPumpFlowRate)
2079 : {
2080 :
2081 : // SUBROUTINE INFORMATION:
2082 : // AUTHOR Edwin Lee
2083 : // DATE WRITTEN July 2010
2084 : // MODIFIED na
2085 : // RE-ENGINEERED na
2086 :
2087 : int PumpIndexStart;
2088 : int PumpIndexEnd;
2089 : int PumpLoopNum;
2090 : DataPlant::LoopSideLocation PumpLoopSideNum;
2091 :
2092 : // If we have a specific loop/side/br/comp, then find the index and only do that one, otherwise do all pumps on the loop side
2093 18662865 : if (present(SpecificPumpLocation)) {
2094 0 : PumpLoopNum = SpecificPumpLocation().loopNum;
2095 0 : PumpLoopSideNum = SpecificPumpLocation().loopSideNum;
2096 0 : int const PumpBranchNum = SpecificPumpLocation().branchNum;
2097 0 : int const PumpCompNum = SpecificPumpLocation().compNum;
2098 0 : PumpIndexStart =
2099 0 : state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum).Branch(PumpBranchNum).Comp(PumpCompNum).IndexInLoopSidePumps;
2100 0 : PumpIndexEnd = PumpIndexStart;
2101 : } else {
2102 18662865 : PumpLoopNum = this->plantLoc.loopNum;
2103 18662865 : PumpLoopSideNum = this->plantLoc.loopSideNum;
2104 18662865 : PumpIndexStart = 1;
2105 18662865 : PumpIndexEnd = this->TotalPumps;
2106 : }
2107 :
2108 : // If we have a flow rate to hit, then go for it, otherwise, just operate in request mode with zero flow
2109 : Real64 FlowToRequest;
2110 18662865 : if (present(SpecificPumpFlowRate)) {
2111 0 : FlowToRequest = SpecificPumpFlowRate;
2112 : } else {
2113 18662865 : FlowToRequest = 0.0;
2114 : }
2115 :
2116 : //~ Now loop through all the pumps and simulate them, keeping track of their status
2117 18662865 : auto &loop_side(state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum));
2118 18662865 : auto &loop_side_branch(loop_side.Branch);
2119 37485789 : for (int PumpCounter = PumpIndexStart; PumpCounter <= PumpIndexEnd; ++PumpCounter) {
2120 :
2121 : //~ Set some variables
2122 18822924 : auto &pump(loop_side.Pumps(PumpCounter));
2123 18822924 : int const PumpBranchNum = pump.BranchNum;
2124 18822924 : int const PumpCompNum = pump.CompNum;
2125 18822924 : int const PumpOutletNode = pump.PumpOutletNode;
2126 :
2127 18822924 : this->AdjustPumpFlowRequestByEMSControls(PumpBranchNum, PumpCompNum, FlowToRequest);
2128 :
2129 : // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
2130 : bool DummyThisPumpRunning;
2131 18822924 : Pumps::SimPumps(state,
2132 18822924 : pump.PumpName,
2133 : PumpLoopNum,
2134 : FlowToRequest,
2135 : DummyThisPumpRunning,
2136 18822924 : loop_side_branch(PumpBranchNum).PumpIndex,
2137 18822924 : pump.PumpHeatToFluid);
2138 :
2139 : //~ Pull some state information from the pump outlet node
2140 18822924 : Real64 const ThisPumpMinAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMinAvail;
2141 18822924 : Real64 const ThisPumpMaxAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMaxAvail;
2142 :
2143 : //~ Now update the data structure
2144 18822924 : pump.CurrentMinAvail = ThisPumpMinAvail;
2145 18822924 : pump.CurrentMaxAvail = ThisPumpMaxAvail;
2146 : }
2147 :
2148 : //~ Update the LoopSide pump heat totality here
2149 18662865 : if (loop_side.TotalPumps > 0) {
2150 18661678 : loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
2151 : }
2152 18662865 : }
2153 :
2154 36566553 : Real64 HalfLoopData::DetermineLoopSideFlowRate(EnergyPlusData &state, int ThisSideInletNode, Real64 ThisSideLoopFlowRequest)
2155 : {
2156 36566553 : Real64 ThisLoopSideFlow = ThisSideLoopFlowRequest;
2157 36566553 : Real64 TotalPumpMinAvailFlow = 0.0;
2158 36566553 : Real64 TotalPumpMaxAvailFlow = 0.0;
2159 36566553 : if (allocated(this->Pumps)) {
2160 :
2161 : //~ Initialize pump values
2162 37482164 : for (auto &e : this->Pumps) {
2163 18821701 : e.CurrentMinAvail = 0.0;
2164 18821701 : e.CurrentMaxAvail = 0.0;
2165 : }
2166 18660463 : this->FlowLock = DataPlant::FlowLock::PumpQuery;
2167 :
2168 : //~ Simulate pumps
2169 18660463 : this->SimulateAllLoopSidePumps(state);
2170 :
2171 : //~ Calculate totals
2172 37482164 : for (auto const &e : this->Pumps) {
2173 18821701 : TotalPumpMinAvailFlow += e.CurrentMinAvail;
2174 18821701 : TotalPumpMaxAvailFlow += e.CurrentMaxAvail;
2175 : }
2176 :
2177 : // Use the pump min/max avail to attempt to constrain the loop side flow
2178 18660463 : ThisLoopSideFlow = PlantUtilities::BoundValueToWithinTwoValues(ThisLoopSideFlow, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
2179 : }
2180 :
2181 : // Now we check flow restriction from the other side, both min and max avail.
2182 : // Doing this last basically means it wins, so the pump should pull down to meet the flow restriction
2183 36566553 : ThisLoopSideFlow = PlantUtilities::BoundValueToNodeMinMaxAvail(state, ThisLoopSideFlow, ThisSideInletNode);
2184 :
2185 : // Final preparation of loop inlet min/max avail if pumps exist
2186 36566553 : if (allocated(this->Pumps)) {
2187 : // At this point, the pump limits should have been obeyed unless a flow restriction was encountered from the other side
2188 : // The pump may, however, have even tighter constraints than the other side
2189 : // At this point, the inlet node doesn't know anything about those limits
2190 : // Since we have already honored the other side flow restriction, try to honor the pump limits here
2191 18660463 : PlantUtilities::TightenNodeMinMaxAvails(state, ThisSideInletNode, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
2192 : }
2193 :
2194 : // Now reset the entering mass flow rate to the decided-upon flow rate
2195 36566553 : state.dataLoopNodes->Node(ThisSideInletNode).MassFlowRate = ThisLoopSideFlow;
2196 36566553 : return ThisLoopSideFlow;
2197 : }
2198 :
2199 72811510 : void HalfLoopData::UpdatePlantMixer(EnergyPlusData &state)
2200 : {
2201 :
2202 : // SUBROUTINE INFORMATION:
2203 : // AUTHOR Brandon Anderson, Dan Fisher
2204 : // DATE WRITTEN October 1999
2205 : // MODIFIED na
2206 : // RE-ENGINEERED na
2207 :
2208 : // PURPOSE OF THIS SUBROUTINE:
2209 : // calculate the outlet conditions at the mixer
2210 : // this is expected to only be called for loops with a mixer
2211 :
2212 : // Find mixer outlet node number
2213 72811510 : int const MixerOutletNode = this->Mixer.NodeNumOut;
2214 :
2215 : // Find corresponding splitter inlet node number--correspondence, but currently
2216 : // hard code things to a single split/mix setting it to the mixer number
2217 72811510 : int const SplitterInNode = this->Splitter.NodeNumIn;
2218 : // Initialize Mixer outlet temp and mass flow rate
2219 72811510 : Real64 MixerOutletTemp = 0.0;
2220 72811510 : Real64 MixerOutletMassFlow = 0.0;
2221 72811510 : Real64 MixerOutletMassFlowMaxAvail = 0.0;
2222 72811510 : Real64 MixerOutletMassFlowMinAvail = 0.0;
2223 72811510 : Real64 MixerOutletPress = 0.0;
2224 72811510 : Real64 MixerOutletQuality = 0.0;
2225 :
2226 : // Calculate Mixer outlet mass flow rate
2227 416849122 : for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
2228 344037612 : int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
2229 344037612 : MixerOutletMassFlow += state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
2230 : }
2231 :
2232 : // Calculate Mixer outlet temperature
2233 276751541 : for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
2234 237431218 : int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
2235 237431218 : if (MixerOutletMassFlow > 0.0) {
2236 203940031 : Real64 const MixerInletMassFlow = state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
2237 203940031 : Real64 const MassFrac = MixerInletMassFlow / MixerOutletMassFlow;
2238 : // mass flow weighted temp and enthalpy for each mixer inlet
2239 203940031 : MixerOutletTemp += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Temp;
2240 203940031 : MixerOutletQuality += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Quality;
2241 203940031 : MixerOutletMassFlowMaxAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMaxAvail;
2242 203940031 : MixerOutletMassFlowMinAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMinAvail;
2243 203940031 : MixerOutletPress = max(MixerOutletPress, state.dataLoopNodes->Node(MixerInletNode).Press);
2244 : } else { // MixerOutletMassFlow <=0, then perform the 'no flow' update.
2245 33491187 : MixerOutletTemp = state.dataLoopNodes->Node(SplitterInNode).Temp;
2246 33491187 : MixerOutletQuality = state.dataLoopNodes->Node(SplitterInNode).Quality;
2247 33491187 : MixerOutletMassFlowMaxAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail;
2248 33491187 : MixerOutletMassFlowMinAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail;
2249 33491187 : MixerOutletPress = state.dataLoopNodes->Node(SplitterInNode).Press;
2250 33491187 : break;
2251 : }
2252 : }
2253 :
2254 72811510 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate = MixerOutletMassFlow;
2255 72811510 : state.dataLoopNodes->Node(MixerOutletNode).Temp = MixerOutletTemp;
2256 72811510 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
2257 : // Don't update pressure, let pressure system handle this...
2258 : } else {
2259 : // Go ahead and update!
2260 72769390 : state.dataLoopNodes->Node(MixerOutletNode).Press = MixerOutletPress;
2261 : }
2262 72811510 : state.dataLoopNodes->Node(MixerOutletNode).Quality = MixerOutletQuality;
2263 :
2264 : // set max/min avails on mixer outlet to be consistent with the following rules
2265 : // 1. limited by the max/min avails on splitter inlet
2266 : // 2. limited by the sum of max/min avails for each branch's mixer inlet node
2267 :
2268 72811510 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMaxAvail =
2269 72811510 : min(MixerOutletMassFlowMaxAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail);
2270 72811510 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMinAvail =
2271 72811510 : max(MixerOutletMassFlowMinAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail);
2272 72811510 : }
2273 :
2274 72811510 : void HalfLoopData::UpdatePlantSplitter(EnergyPlusData &state)
2275 : {
2276 :
2277 : // SUBROUTINE INFORMATION:
2278 : // AUTHOR Brandon Anderson, Dan Fisher
2279 : // DATE WRITTEN October 1999
2280 : // MODIFIED na
2281 : // RE-ENGINEERED na
2282 :
2283 : // PURPOSE OF THIS SUBROUTINE:
2284 : // Set the outlet conditions of the splitter
2285 :
2286 : // Update Temperatures across splitter
2287 72811510 : if (this->Splitter.Exists) {
2288 :
2289 : // Set branch number at splitter inlet
2290 72811510 : int const SplitterInletNode = this->Splitter.NodeNumIn;
2291 :
2292 : // Loop over outlet nodes
2293 416849122 : for (int CurNode = 1; CurNode <= this->Splitter.TotalOutletNodes; ++CurNode) {
2294 344037612 : int const SplitterOutletNode = this->Splitter.NodeNumOut(CurNode);
2295 :
2296 : // Inlet Temp equals exit Temp to all outlet branches
2297 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).Temp = state.dataLoopNodes->Node(SplitterInletNode).Temp;
2298 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).TempMin = state.dataLoopNodes->Node(SplitterInletNode).TempMin;
2299 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).TempMax = state.dataLoopNodes->Node(SplitterInletNode).TempMax;
2300 344037612 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
2301 : // Don't update pressure, let pressure system handle this...
2302 : } else {
2303 : // Go ahead and update!
2304 343983792 : state.dataLoopNodes->Node(SplitterOutletNode).Press = state.dataLoopNodes->Node(SplitterInletNode).Press;
2305 : }
2306 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).Quality = state.dataLoopNodes->Node(SplitterInletNode).Quality;
2307 :
2308 : // These two blocks and the following one which I added need to be cleaned up
2309 : // I think we will always pass maxavail down the splitter, min avail is the issue.
2310 : // Changed to include hardware max in next line
2311 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMaxAvail = min(
2312 344037612 : state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMax);
2313 344037612 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail = 0.0;
2314 :
2315 : // Not sure about passing min avail if it is nonzero. I am testing a pump with nonzero
2316 : // min flow rate, and it is causing problems because this routine passes zero down. Perhaps if
2317 : // it is a single parallel branch, we are safe to assume we need to just pass it down.
2318 : // But need to test for multiple branches (or at least think about it), to see what we need to do...
2319 344037612 : if (this->Splitter.TotalOutletNodes == 1) {
2320 1667936 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail =
2321 1667936 : state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMinAvail;
2322 : }
2323 : }
2324 : }
2325 72811510 : }
2326 :
2327 : } // namespace DataPlant
2328 : } // namespace EnergyPlus
|