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