Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : #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 36200510 : 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 36200510 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
98 36200510 : int ThisSideInletNode = this->NodeNumIn;
99 :
100 36200510 : this->InitialDemandToLoopSetPoint = 0.0;
101 36200510 : this->CurrentAlterationsToDemand = 0.0;
102 36200510 : 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 36200510 : 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 12187666 : PlantCondLoopOperation::InitLoadDistribution(state, FirstHVACIteration);
111 :
112 : // Now that the op scheme types are updated, do LoopSide validation
113 12187666 : this->ValidateFlowControlPaths(state);
114 :
115 : // Set the flag to false so we won't do these again this time step
116 12187666 : this->OncePerTimeStepOperations = false;
117 :
118 : } else {
119 :
120 : // Set the flag to true so that it is activated for the next time step
121 24012844 : this->OncePerTimeStepOperations = true;
122 : }
123 :
124 : // Do pressure system initialize if this is the demand side (therefore once per whole loop)
125 36200510 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
126 18105180 : 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 36200510 : this->TurnOnAllLoopSideBranches();
131 :
132 36200510 : LoopSideLocation OtherLoopSide = static_cast<LoopSideLocation>(LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)]);
133 : // Do the actual simulation here every time
134 36200510 : this->DoFlowAndLoadSolutionPass(state, OtherLoopSide, ThisSideInletNode, FirstHVACIteration);
135 :
136 : // On constant speed branch pump loop sides we need to re-simulate
137 36200510 : if (this->hasConstSpeedBranchPumps) {
138 : // turn off any pumps connected to unloaded equipment and re-do the flow/load solution pass
139 85431 : this->DisableAnyBranchPumpsConnectedToUnloadedEquipment();
140 85431 : 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 36200510 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
145 :
146 : // Pass the loop information via the HVAC interface manager
147 54315540 : HVACInterfaceManager::UpdatePlantLoopInterface(state,
148 18105180 : this->plantLoc,
149 18105180 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut,
150 18105180 : 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 18095330 : 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 54285990 : HVACInterfaceManager::UpdatePlantLoopInterface(state,
161 18095330 : this->plantLoc,
162 18095330 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut,
163 18095330 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn,
164 : ReSimOtherSideNeeded,
165 : thisPlantLoop.CommonPipeType);
166 :
167 : // Update the loop outlet node conditions
168 18095330 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
169 18095330 : .CheckLoopExitNode(state, FirstHVACIteration); // TODO: This is a loop level check, move out
170 :
171 18095330 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
172 18095330 : .UpdateLoopSideReportVars(state, this->InitialDemandToLoopSetPointSAVED, this->LoadToLoopSetPointThatWasntMet);
173 : }
174 36200510 : }
175 :
176 12187666 : 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 12187666 : int constexpr Parallel(1);
216 12187666 : int constexpr Outlet(2);
217 :
218 : //~ Initialze
219 12187666 : bool EncounteredLRB = false;
220 12187666 : bool EncounteredNonLRBAfterLRB = false;
221 12187666 : 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 12187666 : int firstBranchIndex = 1;
230 24580420 : for (int CompIndex = 1; CompIndex <= this->Branch(firstBranchIndex).TotalComponents; ++CompIndex) {
231 :
232 12392754 : auto const &this_component(this->Branch(firstBranchIndex).Comp(CompIndex));
233 :
234 : {
235 12392754 : switch (this_component.CurOpSchemeType) {
236 2872 : case OpScheme::HeatingRB:
237 : case OpScheme::CoolingRB: { //~ load range based
238 2872 : 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 2872 : EncounteredLRB = true;
246 : }
247 2872 : break;
248 : }
249 6179905 : 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 6179905 : break;
253 : }
254 6125106 : 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 6125106 : 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 84871 : default: { //~ Other control type
266 84871 : 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 12187666 : 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 65114993 : for (int PathCounter = 1; PathCounter <= NumParallelPaths; ++PathCounter) {
286 158995035 : for (int ParallelOrOutletIndex = Parallel; ParallelOrOutletIndex <= Outlet; ++ParallelOrOutletIndex) {
287 : int BranchIndex;
288 105996690 : if (ParallelOrOutletIndex == Parallel) {
289 : // The branch index will be the current pathtype + 1 to add the inlet branch
290 52998345 : BranchIndex = PathCounter + 1;
291 : } else { // ParallelOrOutletIndex == Outlet
292 : // The branch index will be the LoopSide outlet node
293 52998345 : 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 212411022 : for (int CompIndex = 1; CompIndex <= this->Branch(BranchIndex).TotalComponents; ++CompIndex) {
298 :
299 106414332 : auto const &this_component(this->Branch(BranchIndex).Comp(CompIndex));
300 :
301 : {
302 106414332 : switch (this_component.CurOpSchemeType) {
303 6464652 : case OpScheme::HeatingRB:
304 : case OpScheme::CoolingRB: { //~ load range based
305 6464652 : 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 6464652 : EncounteredLRB = true;
313 : }
314 6464652 : break;
315 : }
316 :
317 64691972 : 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 64691972 : break;
320 : }
321 90330 : 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 90330 : 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 35167378 : default: { //~ Other control type
335 35167378 : 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 113875800 : 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 113875800 : if (FirstHVACIteration) {
382 50485122 : return false;
383 : }
384 :
385 63390678 : InletAvgTemp = sum(this->InletNode.TemperatureHistory) / size(this->InletNode.TemperatureHistory);
386 63390678 : if (any_ne(this->InletNode.TemperatureHistory, InletAvgTemp)) {
387 62964601 : return false;
388 : }
389 :
390 426077 : InletAvgMdot = sum(this->InletNode.MassFlowRateHistory) / size(this->InletNode.MassFlowRateHistory);
391 426077 : if (any_ne(this->InletNode.MassFlowRateHistory, InletAvgMdot)) {
392 173944 : return false;
393 : }
394 :
395 252133 : OutletAvgTemp = sum(this->OutletNode.TemperatureHistory) / size(this->OutletNode.TemperatureHistory);
396 252133 : if (any_ne(this->OutletNode.TemperatureHistory, OutletAvgTemp)) {
397 77055 : return false;
398 : }
399 :
400 175078 : OutletAvgMdot = sum(this->OutletNode.MassFlowRateHistory) / size(this->OutletNode.MassFlowRateHistory);
401 175078 : if (any_ne(this->OutletNode.MassFlowRateHistory, OutletAvgMdot)) {
402 6870 : return false;
403 : }
404 :
405 : // If we made it this far, we're good!
406 168208 : return true;
407 : }
408 :
409 113875800 : 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 : DataPlant::PlantEquipmentType ComponentType;
450 : Real64 MassFlowRateFound;
451 : Real64 MassFlow;
452 : bool PlantIsRigid;
453 :
454 113875800 : auto &this_branch(this->Branch(BranchNum));
455 :
456 113875800 : BranchInletNode = this_branch.NodeNumIn;
457 :
458 : //~ Possible error handling if needed
459 113875800 : if (ValueToPush != state.dataLoopNodes->Node(BranchInletNode).MassFlowRate) {
460 : // Diagnostic problem, flow resolver isn't calling this routine properly
461 : }
462 :
463 : //~ This section would really be useful more later on if this routine has more logic regarding what to push down the branch
464 113875800 : MassFlow = ValueToPush;
465 : // MinAvail = ValueToPush
466 : // MaxAvail = ValueToPush
467 :
468 113875800 : PlantIsRigid = this->CheckPlantConvergence(FirstHVACIteration);
469 :
470 : //~ Loop across all component outlet nodes and update their mass flow and max avail
471 228359972 : for (CompCounter = 1; CompCounter <= this_branch.TotalComponents; ++CompCounter) {
472 :
473 114484172 : auto const &this_comp(this_branch.Comp(CompCounter));
474 :
475 : //~ Pick up some values for convenience
476 114484172 : int ComponentInletNode = this_comp.NodeNumIn;
477 114484172 : int ComponentOutletNode = this_comp.NodeNumOut;
478 114484172 : MassFlowRateFound = state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate;
479 114484172 : ComponentType = this_comp.Type;
480 :
481 : //~ Push the values through
482 114484172 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRate = MassFlow;
483 :
484 114484172 : if (PlantIsRigid) {
485 169328 : state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMinAvail = MassFlow;
486 169328 : state.dataLoopNodes->Node(ComponentInletNode).MassFlowRateMaxAvail = MassFlow;
487 169328 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMinAvail = MassFlow;
488 169328 : state.dataLoopNodes->Node(ComponentOutletNode).MassFlowRateMaxAvail = MassFlow;
489 : }
490 : // Node(ComponentOutletNode)%MassFlowRateMinAvail = MinAvail
491 : // no this is 2-way valve which messes up flow options
492 : // for demand components Node(ComponentOutletNode)%MassFlowRateMaxAvail = MaxAvail
493 :
494 : //~ If this value matches then we are good to move to the next component
495 114484172 : if (std::abs(MassFlow - MassFlowRateFound) < CriteriaDelta_MassFlowRate) continue;
496 : //~ Since there is a difference, we have to decide what to do based on the component type:
497 : //~ For plant connections, don't do anything, it SHOULD work itself out
498 : //~ For air connections, trip the LoopSide air flag
499 : //~ Similar for zone, none zone, and electric load center
500 : {
501 12058625 : switch (ComponentType) {
502 :
503 : // possibly air-connected components
504 858487 : case DataPlant::PlantEquipmentType::CoilWaterCooling:
505 : case DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling:
506 : case DataPlant::PlantEquipmentType::CoilWaterSimpleHeating:
507 : case DataPlant::PlantEquipmentType::CoilSteamAirHeating:
508 : case DataPlant::PlantEquipmentType::CoilWAHPHeatingEquationFit:
509 : case DataPlant::PlantEquipmentType::CoilWAHPCoolingEquationFit:
510 : case DataPlant::PlantEquipmentType::CoilWAHPHeatingParamEst:
511 : case DataPlant::PlantEquipmentType::CoilWAHPCoolingParamEst:
512 : case DataPlant::PlantEquipmentType::CoilUserDefined:
513 : case DataPlant::PlantEquipmentType::CoilVSWAHPCoolingEquationFit:
514 : case DataPlant::PlantEquipmentType::CoilVSWAHPHeatingEquationFit:
515 : case DataPlant::PlantEquipmentType::PackagedTESCoolingCoil: {
516 858487 : this->SimAirLoopsNeeded = true;
517 : // sometimes these coils are children in ZoneHVAC equipment
518 : // PlantLoop(LoopNum)%LoopSide(LoopSideNum)%SimZoneEquipNeeded= .TRUE.
519 858487 : break;
520 : }
521 :
522 : // zone connected components
523 24458 : case DataPlant::PlantEquipmentType::CoolingPanel_Simple:
524 : case DataPlant::PlantEquipmentType::Baseboard_Conv_Water:
525 : case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Steam:
526 : case DataPlant::PlantEquipmentType::Baseboard_Rad_Conv_Water:
527 : case DataPlant::PlantEquipmentType::LowTempRadiant_VarFlow:
528 : case DataPlant::PlantEquipmentType::LowTempRadiant_ConstFlow:
529 : case DataPlant::PlantEquipmentType::CooledBeamAirTerminal:
530 : case DataPlant::PlantEquipmentType::ZoneHVACAirUserDefined:
531 : case DataPlant::PlantEquipmentType::AirTerminalUserDefined:
532 : case DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal: {
533 24458 : this->SimZoneEquipNeeded = true;
534 24458 : break;
535 : }
536 :
537 : // electric center connected components
538 5751 : case DataPlant::PlantEquipmentType::Generator_FCExhaust:
539 : case DataPlant::PlantEquipmentType::Generator_FCStackCooler:
540 : case DataPlant::PlantEquipmentType::Generator_MicroCHP:
541 : case DataPlant::PlantEquipmentType::Generator_MicroTurbine:
542 : case DataPlant::PlantEquipmentType::Generator_ICEngine:
543 : case DataPlant::PlantEquipmentType::Generator_CTurbine: {
544 5751 : this->SimElectLoadCentrNeeded = true;
545 5751 : break;
546 : }
547 :
548 11169929 : default:
549 11169929 : break;
550 : }
551 : }
552 : }
553 113875800 : }
554 :
555 36200510 : void HalfLoopData::TurnOnAllLoopSideBranches()
556 : {
557 194186306 : for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
558 157985796 : auto &branch = this->Branch(branchNum);
559 157985796 : branch.disableOverrideForCSBranchPumping = false;
560 : }
561 36200510 : }
562 :
563 72571882 : void HalfLoopData::SimulateAllLoopSideBranches(EnergyPlusData &state,
564 : Real64 const ThisLoopSideFlow,
565 : bool const FirstHVACIteration,
566 : bool &LoopShutDownFlag)
567 : {
568 :
569 : // SUBROUTINE INFORMATION:
570 : // AUTHOR Edwin Lee
571 : // DATE WRITTEN July 2010
572 : // MODIFIED na
573 : // RE-ENGINEERED na
574 :
575 : // PURPOSE OF THIS SUBROUTINE:
576 : // This routine will step through all branch groups (single branch .OR. inlet/parallels/outlet)
577 : // and call the branch group simulation routine. This routine also calls to update the splitter
578 : // and mixer.
579 :
580 : // METHODOLOGY EMPLOYED:
581 : // The number of branch groups is either 1 or 3. 1 would be a single branch half-loop. 3 would
582 : // be the minimum for an inlet/parallels/outlet set. The number of branch groups can then be
583 : // calculated as #BrGrps = 1 + 2*L; where L is zero for single half loop and one for parallel-type set.
584 : // This calculation can be reduced to the logical/integer conversion as shown in the code.
585 : // The simulation then steps through each branch group. If there are parallel branches, the splitter is
586 : // updated on flowlock=0 to pass information through, then after the parallel branches the mixer is always
587 : // updated. The outlet branch "group" is then simulated.
588 :
589 : // SUBROUTINE PARAMETER DEFINITIONS:
590 72571882 : int constexpr InletBranchOrOneBranchHalfLoop(1);
591 72571882 : int constexpr ParallelBranchSet(2);
592 72571882 : int constexpr OutletBranch(3);
593 :
594 72571882 : int NumBranchGroups = 1;
595 72571882 : if (this->TotalBranches > 1) {
596 72250166 : NumBranchGroups = 3;
597 : }
598 :
599 : // reset branch starting component index back to zero before each pass
600 533856184 : for (int BranchCounter = 1; BranchCounter <= this->TotalBranches; ++BranchCounter) {
601 461284302 : this->Branch(BranchCounter).lastComponentSimulated = 0;
602 : }
603 :
604 289644096 : for (int BranchGroup = 1; BranchGroup <= NumBranchGroups; ++BranchGroup) {
605 :
606 217072214 : if ((BranchGroup > 1) && (this->TotalBranches == 1)) break;
607 :
608 217072214 : switch (BranchGroup) {
609 72571882 : case InletBranchOrOneBranchHalfLoop:
610 72571882 : this->SimulateLoopSideBranchGroup(state, 1, 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
611 72571882 : break;
612 72250166 : case ParallelBranchSet:
613 72250166 : this->UpdatePlantSplitter(state);
614 72250166 : this->SimulateLoopSideBranchGroup(state, 2, this->TotalBranches - 1, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
615 72250166 : this->UpdatePlantMixer(state);
616 72250166 : break;
617 72250166 : case OutletBranch:
618 72250166 : this->SimulateLoopSideBranchGroup(
619 : state, this->TotalBranches, this->TotalBranches, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
620 72250166 : break;
621 : }
622 : }
623 72571882 : }
624 :
625 56107969 : void HalfLoopData::AdjustPumpFlowRequestByEMSControls(int const BranchNum, int const CompNum, Real64 &FlowToRequest)
626 : {
627 :
628 : // SUBROUTINE INFORMATION:
629 : // AUTHOR Brent Griffith
630 : // DATE WRITTEN April 2012
631 : // MODIFIED na
632 : // RE-ENGINEERED na
633 :
634 : // PURPOSE OF THIS SUBROUTINE:
635 : // modify flow request to pump simulation if EMS is overriding pump component
636 :
637 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
638 56107969 : auto &this_branch(this->Branch(BranchNum));
639 56107969 : auto &this_comp(this_branch.Comp(CompNum));
640 :
641 56107969 : if ((this->EMSCtrl) && (this->EMSValue <= 0.0)) {
642 0 : FlowToRequest = 0.0;
643 0 : return;
644 : }
645 :
646 56107969 : if ((this_branch.EMSCtrlOverrideOn) && (this_branch.EMSCtrlOverrideValue <= 0.0)) {
647 0 : FlowToRequest = 0.0;
648 0 : return;
649 : }
650 :
651 56107969 : if (this_comp.EMSLoadOverrideOn) {
652 334139 : if (this_comp.EMSLoadOverrideValue == 0.0) {
653 253511 : FlowToRequest = 0.0;
654 : }
655 : }
656 : }
657 :
658 85431 : void HalfLoopData::DisableAnyBranchPumpsConnectedToUnloadedEquipment()
659 : {
660 330762 : for (int branchNum = 2; branchNum <= this->TotalBranches - 1; ++branchNum) {
661 245331 : auto &branch = this->Branch(branchNum);
662 245331 : Real64 totalDispatchedLoadOnBranch = 0.0;
663 661524 : for (int compNum = 1; compNum <= branch.TotalComponents; ++compNum) {
664 416193 : auto const &component = branch.Comp(compNum);
665 416193 : auto const &t = component.Type;
666 416193 : if (t == DataPlant::PlantEquipmentType::PumpConstantSpeed || t == DataPlant::PlantEquipmentType::PumpBankConstantSpeed ||
667 245331 : t == DataPlant::PlantEquipmentType::PumpVariableSpeed || t == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
668 : // don't do anything
669 : } else {
670 245331 : totalDispatchedLoadOnBranch += component.MyLoad;
671 : }
672 : }
673 245331 : if (std::abs(totalDispatchedLoadOnBranch) < 0.001) {
674 192042 : branch.disableOverrideForCSBranchPumping = true;
675 : }
676 : }
677 85431 : }
678 :
679 36285941 : Real64 HalfLoopData::EvaluateLoopSetPointLoad(EnergyPlusData &state, int const FirstBranchNum, int const LastBranchNum, Real64 ThisLoopSideFlow)
680 : {
681 :
682 : // FUNCTION INFORMATION:
683 : // AUTHOR Edwin Lee
684 : // DATE WRITTEN August 2010
685 : // MODIFIED na
686 : // RE-ENGINEERED na
687 :
688 : // Return value
689 36285941 : Real64 LoadToLoopSetPoint = 0.0; // function result
690 :
691 : static constexpr std::string_view RoutineName("PlantLoopSolver::EvaluateLoopSetPointLoad");
692 : static constexpr std::string_view RoutineNameAlt("PlantSupplySide:EvaluateLoopSetPointLoad");
693 :
694 : //~ General variables
695 36285941 : Real64 SumMdotTimesTemp = 0.0;
696 36285941 : Real64 SumMdot = 0.0;
697 :
698 36285941 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
699 :
700 : // We will place one specialized case in here for common pipe simulations.
701 : // If we are doing a common pipe simulation, and there is greater other-side flow than this side,
702 : // then the "other side" demand needs to include getting the flow through the common pipe to the same setpoint
703 : // as the flow going through the actual supply side
704 36285941 : if (this->hasConstSpeedBranchPumps && this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply &&
705 120503 : thisPlantLoop.CommonPipeType != DataPlant::CommonPipeType::No) {
706 19955 : const DataPlant::LoopSideLocation OtherSide = LoopSideOther[static_cast<int>(this->plantLoc.loopSideNum)];
707 19955 : const int otherSideOutletNodeNum = thisPlantLoop.LoopSide(OtherSide).NodeNumOut;
708 19955 : Real64 commonPipeFlow = state.dataLoopNodes->Node(otherSideOutletNodeNum).MassFlowRate - ThisLoopSideFlow;
709 19955 : Real64 otherSideExitingTemperature = state.dataLoopNodes->Node(otherSideOutletNodeNum).Temp;
710 19955 : SumMdotTimesTemp += otherSideExitingTemperature * commonPipeFlow;
711 19955 : SumMdot += commonPipeFlow;
712 : }
713 :
714 : // Sweep across flow paths in this group and calculate the deltaT and then the load
715 36285941 : int BranchIndex = 0; // ~ This is a 1 - n value within the current branch group
716 72571882 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
717 :
718 36285941 : ++BranchIndex;
719 :
720 : //~ Always start from the last component we did the last time around + 1 and
721 : //~ try to make it all the way to the end of the loop
722 36285941 : int StartingComponent = this->Branch(BranchCounter).lastComponentSimulated + 1;
723 36285941 : int EnteringNodeNum = this->Branch(BranchCounter).Comp(StartingComponent).NodeNumIn;
724 :
725 36285941 : Real64 EnteringTemperature = state.dataLoopNodes->Node(EnteringNodeNum).Temp;
726 36285941 : Real64 MassFlowRate = state.dataLoopNodes->Node(EnteringNodeNum).MassFlowRate;
727 :
728 36285941 : SumMdotTimesTemp += EnteringTemperature * MassFlowRate;
729 36285941 : SumMdot += MassFlowRate;
730 : }
731 :
732 36285941 : if (SumMdot < DataBranchAirLoopPlant::MassFlowTolerance) {
733 16977386 : return 0.0;
734 : }
735 :
736 19308555 : Real64 WeightedInletTemp = SumMdotTimesTemp / SumMdot;
737 :
738 19308555 : if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Water) {
739 :
740 : Real64 Cp =
741 19270517 : FluidProperties::GetSpecificHeatGlycol(state, thisPlantLoop.FluidName, WeightedInletTemp, thisPlantLoop.FluidIndex, RoutineName);
742 :
743 : {
744 :
745 19270517 : if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
746 :
747 : // Pick up the loop setpoint temperature
748 18929009 : Real64 LoopSetPointTemperature = this->TempSetPoint;
749 : // Calculate the delta temperature
750 18929009 : Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
751 :
752 : // Calculate the demand on the loop
753 18929009 : LoadToLoopSetPoint = SumMdot * Cp * DeltaTemp;
754 :
755 341508 : } else if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
756 :
757 : // Get the range of setpoints
758 341508 : Real64 LoopSetPointTemperatureHi = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointHi;
759 341508 : Real64 LoopSetPointTemperatureLo = state.dataLoopNodes->Node(thisPlantLoop.TempSetPointNodeNum).TempSetPointLo;
760 :
761 : // Calculate the demand on the loop
762 341508 : if (SumMdot > 0.0) {
763 341508 : Real64 LoadToHeatingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureLo - WeightedInletTemp);
764 341508 : Real64 LoadToCoolingSetPoint = SumMdot * Cp * (LoopSetPointTemperatureHi - WeightedInletTemp);
765 : // Possible combinations:
766 : // 1 LoadToHeatingSetPoint > 0 & LoadToCoolingSetPoint > 0 --> Heating required
767 : // 2 LoadToHeatingSetPoint < 0 & LoadToCoolingSetPoint < 0 --> Cooling Required
768 : // 3 LoadToHeatingSetPoint <=0 & LoadToCoolingSetPoint >=0 --> Dead Band Operation - includes zero load cases
769 : // 4 LoadToHeatingSetPoint > LoadToCoolingSetPoint --> Not Feasible if LoopSetPointHi >= LoopSetPointLo
770 : // First trap bad set-points
771 341508 : if (LoadToHeatingSetPoint > LoadToCoolingSetPoint) {
772 0 : ShowSevereError(state,
773 : "Plant Loop: the Plant Loop Demand Calculation Scheme is set to DualSetPointDeadBand, but the "
774 : "heating-related low setpoint appears to be above the cooling-related high setpoint.");
775 0 : ShowContinueError(state,
776 : "For example, if using SetpointManager:Scheduled:DualSetpoint, then check that the low setpoint is "
777 : "below the high setpoint.");
778 0 : ShowContinueError(state, "Occurs in PlantLoop=" + thisPlantLoop.Name);
779 0 : ShowContinueError(
780 : state,
781 0 : format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
782 0 : ShowContinueError(state, format("Loop Heating Low Setpoint={:.2R}", LoopSetPointTemperatureLo));
783 0 : ShowContinueError(state, format("Loop Cooling High Setpoint={:.2R}", LoopSetPointTemperatureHi));
784 :
785 0 : ShowFatalError(state, "Program terminates due to above conditions.");
786 : }
787 341508 : if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
788 86954 : LoadToLoopSetPoint = LoadToHeatingSetPoint;
789 254554 : } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
790 147108 : LoadToLoopSetPoint = LoadToCoolingSetPoint;
791 107446 : } else if (LoadToHeatingSetPoint <= 0.0 && LoadToCoolingSetPoint >= 0.0) { // deadband includes zero loads
792 107446 : LoadToLoopSetPoint = 0.0;
793 : } else {
794 0 : ShowSevereError(state,
795 : "DualSetPointWithDeadBand: Unanticipated combination of heating and cooling loads - report to EnergyPlus "
796 : "Development Team");
797 0 : ShowContinueError(state, "occurs in PlantLoop=" + thisPlantLoop.Name);
798 0 : ShowContinueError(
799 : state,
800 0 : format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
801 0 : ShowContinueError(state, format("Loop Heating Setpoint={:.2R}", LoopSetPointTemperatureLo));
802 0 : ShowContinueError(state, format("Loop Cooling Setpoint={:.2R}", LoopSetPointTemperatureHi));
803 0 : ShowFatalError(state, "Program terminates due to above conditions.");
804 : }
805 : } else {
806 0 : LoadToLoopSetPoint = 0.0;
807 : }
808 : }
809 : }
810 :
811 38038 : } else if (thisPlantLoop.FluidType == DataLoopNode::NodeFluidType::Steam) {
812 :
813 : Real64 Cp =
814 38038 : FluidProperties::GetSpecificHeatGlycol(state, thisPlantLoop.FluidName, WeightedInletTemp, thisPlantLoop.FluidIndex, RoutineName);
815 :
816 : {
817 :
818 38038 : if (thisPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
819 :
820 : // Pick up the loop setpoint temperature
821 38038 : Real64 LoopSetPointTemperature = this->TempSetPoint;
822 :
823 : // Calculate the delta temperature
824 38038 : Real64 DeltaTemp = LoopSetPointTemperature - WeightedInletTemp;
825 :
826 : Real64 EnthalpySteamSatVapor =
827 38038 : FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, LoopSetPointTemperature, 1.0, this->refrigIndex, RoutineNameAlt);
828 : Real64 EnthalpySteamSatLiquid =
829 38038 : FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, LoopSetPointTemperature, 0.0, this->refrigIndex, RoutineNameAlt);
830 :
831 38038 : Real64 LatentHeatSteam = EnthalpySteamSatVapor - EnthalpySteamSatLiquid;
832 :
833 : // Calculate the demand on the loop
834 38038 : LoadToLoopSetPoint = SumMdot * (Cp * DeltaTemp + LatentHeatSteam);
835 : }
836 : }
837 :
838 : } else { // only have two types, water serves for glycol.
839 : }
840 :
841 : // Trim the demand to zero if it is very small
842 19308555 : if (std::abs(LoadToLoopSetPoint) < DataPlant::LoopDemandTol) LoadToLoopSetPoint = 0.0;
843 :
844 19308555 : return LoadToLoopSetPoint;
845 : }
846 :
847 36285941 : Real64 HalfLoopData::CalcOtherSideDemand(EnergyPlusData &state, Real64 ThisLoopSideFlow)
848 : {
849 :
850 : // FUNCTION INFORMATION:
851 : // AUTHOR Edwin Lee
852 : // DATE WRITTEN August 2010
853 : // MODIFIED na
854 : // RE-ENGINEERED na
855 :
856 : // PURPOSE OF THIS FUNCTION:
857 : // To evaluate the demand to hit the loop setpoint based on the loop side inlet conditions
858 :
859 : // METHODOLOGY EMPLOYED:
860 : // This routine will simply call the evaluate loop setpoint routine but call it from
861 : // the very beginning of this loop side, so that it is basically for the entire loop side
862 :
863 : // FUNCTION PARAMETER DEFINITIONS:
864 36285941 : return this->EvaluateLoopSetPointLoad(state, 1, 1, ThisLoopSideFlow);
865 : }
866 :
867 36285941 : Real64 HalfLoopData::SetupLoopFlowRequest(EnergyPlusData &state, const LoopSideLocation OtherSide)
868 : {
869 :
870 : // FUNCTION INFORMATION:
871 : // AUTHOR: Dan Fisher, Edwin Lee
872 : // DATE WRITTEN: August 2010
873 : // MODIFIED: na
874 : // RE-ENGINEERED: na
875 :
876 : // PURPOSE OF THIS SUBROUTINE:
877 : // This routine sets up the flow request values and sums them up for each loop side
878 : // Then makes a decision on the desired loop flow based on loop configuration
879 :
880 : // METHODOLOGY EMPLOYED:
881 : // Scan through the components on this loop side, and look at the mass flow request
882 : // values on components inlet node.
883 : // Check common pipe/pumping configuration for this loop side and the other loop side
884 : // to determine what the LoopSide should flow
885 :
886 : //~ Initialize
887 36285941 : Real64 LoopFlow = 0.0; // Once all flow requests are evaluated, this is the desired flow on this side
888 :
889 : // reference
890 36285941 : auto &loop(state.dataPlnt->PlantLoop(this->plantLoc.loopNum));
891 :
892 : //~ First we need to set up the flow requests on each LoopSide
893 108857823 : for (DataPlant::LoopSideLocation LoopSideCounter : DataPlant::LoopSideKeys) {
894 : // Clear things out for this LoopSide
895 72571882 : Real64 InletBranchRequestNeedAndTurnOn = 0.0;
896 72571882 : Real64 InletBranchRequestNeedIfOn = 0.0;
897 72571882 : Real64 ParallelBranchRequestsNeedAndTurnOn(0.0);
898 72571882 : Real64 ParallelBranchRequestsNeedIfOn(0.0);
899 72571882 : Real64 OutletBranchRequestNeedAndTurnOn = 0.0;
900 72571882 : Real64 OutletBranchRequestNeedIfOn = 0.0;
901 :
902 : // reference
903 72571882 : auto &loop_side(loop.LoopSide(LoopSideCounter));
904 :
905 72571882 : loop_side.flowRequestNeedIfOn = 0.0;
906 72571882 : loop_side.flowRequestNeedAndTurnOn = 0.0;
907 72571882 : loop_side.flowRequestFinal = 0.0;
908 72571882 : loop_side.hasConstSpeedBranchPumps = false;
909 :
910 : // Now loop through all the branches on this LoopSide and get flow requests
911 72571882 : int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
912 72571882 : int ParallelBranchIndex = 0;
913 533743694 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
914 461171812 : Real64 ThisBranchFlowRequestNeedAndTurnOn = 0.0;
915 461171812 : Real64 ThisBranchFlowRequestNeedIfOn = 0.0;
916 :
917 : // reference
918 461171812 : auto &branch(loop_side.Branch(BranchCounter));
919 :
920 461171812 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) ++ParallelBranchIndex;
921 :
922 461171812 : if (branch.disableOverrideForCSBranchPumping) {
923 384072 : branch.RequestedMassFlow = 0.0;
924 384072 : continue;
925 : }
926 :
927 460787740 : int const NumCompsOnThisBranch = branch.TotalComponents;
928 924477192 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
929 :
930 : // reference
931 463689452 : auto &component(branch.Comp(CompCounter));
932 :
933 463689452 : int NodeToCheckRequest = component.NodeNumIn;
934 463689452 : LoopFlowStatus FlowPriorityStatus = component.FlowPriority;
935 :
936 : // reference
937 463689452 : auto &node_with_request(state.dataLoopNodes->Node(NodeToCheckRequest));
938 :
939 463689452 : if (!DataPlant::PlantEquipmentTypeIsPump[static_cast<int>(component.Type)]) {
940 :
941 426670137 : if (FlowPriorityStatus == DataPlant::LoopFlowStatus::Invalid) {
942 : // do nothing
943 426655601 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyAndTurnsLoopOn) {
944 203031851 : ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, node_with_request.MassFlowRateRequest);
945 203031851 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
946 223623750 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::NeedyIfLoopOn) {
947 20595438 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
948 : } else if (FlowPriorityStatus == DataPlant::LoopFlowStatus::TakesWhatGets) {
949 : // do nothing
950 : }
951 : } else { // handle pumps differently
952 37019315 : if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
953 35866166 : (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay)) {
954 : // special primary side flow request for two way common pipe
955 409178 : int const CompIndex = component.CompNum;
956 409178 : switch (component.Type) {
957 : // remove var speed pumps from this case statement if can set MassFlowRateRequest
958 409178 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
959 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
960 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
961 409178 : if (CompIndex > 0) {
962 : ThisBranchFlowRequestNeedIfOn =
963 409178 : max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
964 : }
965 409178 : break;
966 0 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
967 0 : if (CompIndex > 0) {
968 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
969 0 : state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
970 0 : state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
971 : }
972 0 : break;
973 0 : default:
974 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
975 0 : break;
976 : }
977 :
978 37019315 : } else if ((BranchCounter == 1) && (LoopSideCounter == DataPlant::LoopSideLocation::Supply) &&
979 35456988 : (loop.CommonPipeType == DataPlant::CommonPipeType::Single)) {
980 255612 : int const CompIndex = component.CompNum;
981 255612 : switch (component.Type) {
982 : // remove var speed pumps from this case statement if can set MassFlowRateRequest
983 255612 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
984 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
985 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed: {
986 255612 : if (CompIndex > 0) {
987 : ThisBranchFlowRequestNeedIfOn =
988 255612 : max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax);
989 : }
990 255612 : break;
991 : }
992 0 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
993 0 : if (CompIndex > 0) {
994 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn,
995 0 : state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax /
996 0 : state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank);
997 : }
998 0 : break;
999 0 : default:
1000 0 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, node_with_request.MassFlowRateRequest);
1001 : }
1002 255612 : } else {
1003 36354525 : int const CompIndex = component.CompNum;
1004 36354525 : switch (component.Type) {
1005 6249696 : case DataPlant::PlantEquipmentType::PumpConstantSpeed:
1006 6249696 : if (CompIndex > 0) {
1007 6249696 : auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
1008 6249696 : if (ParallelBranchIndex >= 1) { // branch pump
1009 277447 : if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1010 129539 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1011 147908 : } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
1012 18140 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1013 : }
1014 277447 : loop_side.hasConstSpeedBranchPumps = true;
1015 277447 : branch.HasConstantSpeedBranchPump = true;
1016 277447 : branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax;
1017 : } else { // inlet pump
1018 5972249 : ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax);
1019 : }
1020 : }
1021 6249696 : break;
1022 14520 : case DataPlant::PlantEquipmentType::PumpBankConstantSpeed:
1023 14520 : if (CompIndex > 0) {
1024 14520 : auto const &this_pump(state.dataPumps->PumpEquip(CompIndex));
1025 14520 : if (ParallelBranchIndex >= 1) { // branch pump
1026 0 : if (branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1027 : ThisBranchFlowRequestNeedIfOn =
1028 0 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1029 0 : } else if (loop.CommonPipeType != DataPlant::CommonPipeType::No) { // common pipe and constant branch pumps
1030 : ThisBranchFlowRequestNeedIfOn =
1031 0 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1032 : }
1033 0 : loop_side.hasConstSpeedBranchPumps = true;
1034 0 : branch.HasConstantSpeedBranchPump = true;
1035 0 : branch.ConstantSpeedBranchMassFlow = this_pump.MassFlowRateMax / this_pump.NumPumpsInBank;
1036 : } else { // inlet pump
1037 : ThisBranchFlowRequestNeedIfOn =
1038 14520 : max(ThisBranchFlowRequestNeedIfOn, this_pump.MassFlowRateMax / this_pump.NumPumpsInBank);
1039 : }
1040 : }
1041 14520 : break;
1042 :
1043 : // overwrite here for branch pumps
1044 30090309 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
1045 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
1046 : case DataPlant::PlantEquipmentType::PumpCondensate:
1047 30090309 : if (component.CompNum > 0) {
1048 30090309 : auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
1049 30090309 : this_pump.LoopSolverOverwriteFlag = false;
1050 : }
1051 : default:
1052 30090309 : break;
1053 : }
1054 : }
1055 : }
1056 : }
1057 460787740 : if (BranchCounter == 1) { // inlet branch
1058 72571882 : InletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
1059 72571882 : InletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
1060 388215858 : } else if (BranchCounter < NumBranchesOnThisLoopSide) { // branchcounter = 1 is already caught
1061 315968464 : ParallelBranchRequestsNeedAndTurnOn += ThisBranchFlowRequestNeedAndTurnOn;
1062 315968464 : ParallelBranchRequestsNeedIfOn += ThisBranchFlowRequestNeedIfOn;
1063 72247394 : } else if (BranchCounter == NumBranchesOnThisLoopSide) { // outlet branch
1064 72247394 : OutletBranchRequestNeedAndTurnOn = ThisBranchFlowRequestNeedAndTurnOn;
1065 72247394 : OutletBranchRequestNeedIfOn = ThisBranchFlowRequestNeedIfOn;
1066 : }
1067 :
1068 460787740 : branch.RequestedMassFlow = max(ThisBranchFlowRequestNeedIfOn, ThisBranchFlowRequestNeedAndTurnOn);
1069 : }
1070 72571882 : loop_side.flowRequestNeedAndTurnOn =
1071 72571882 : max(InletBranchRequestNeedAndTurnOn, ParallelBranchRequestsNeedAndTurnOn, OutletBranchRequestNeedAndTurnOn);
1072 72571882 : loop_side.flowRequestNeedIfOn = max(InletBranchRequestNeedIfOn, ParallelBranchRequestsNeedIfOn, OutletBranchRequestNeedIfOn);
1073 : }
1074 :
1075 36285941 : auto &this_loop_side(loop.LoopSide(this->plantLoc.loopSideNum));
1076 36285941 : auto &other_loop_side(loop.LoopSide(OtherSide));
1077 :
1078 : //~ Now that we have calculated each sides different status's requests, process to find final
1079 36285941 : if ((this_loop_side.flowRequestNeedAndTurnOn + other_loop_side.flowRequestNeedAndTurnOn) < DataBranchAirLoopPlant::MassFlowTolerance) {
1080 16975360 : this_loop_side.flowRequestFinal = 0.0;
1081 16975360 : other_loop_side.flowRequestFinal = 0.0;
1082 : } else { // some flow is needed and loop should try to run
1083 19310581 : this_loop_side.flowRequestFinal = max(this_loop_side.flowRequestNeedAndTurnOn, this_loop_side.flowRequestNeedIfOn);
1084 19310581 : other_loop_side.flowRequestFinal = max(other_loop_side.flowRequestNeedAndTurnOn, other_loop_side.flowRequestNeedIfOn);
1085 : }
1086 : // now store final flow requests on each loop side data structure
1087 36285941 : this_loop_side.FlowRequest = this_loop_side.flowRequestFinal;
1088 36285941 : other_loop_side.FlowRequest = other_loop_side.flowRequestFinal;
1089 :
1090 36285941 : if (loop.CommonPipeType == DataPlant::CommonPipeType::No) {
1091 : // we may or may not have a pump on this side, but the flow request is the larger of the two side's final
1092 35588265 : if ((!this_loop_side.hasConstSpeedBranchPumps) && (!other_loop_side.hasConstSpeedBranchPumps)) {
1093 35461638 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1094 : } else { // account for stepped loop flow rates required of branch pumps
1095 :
1096 : // rules for setting flow when there are constant speed branch pumps.
1097 : // 1. Check if above routines already selected a loop flow rate based on the constant speed branches, if so then just use it
1098 126627 : if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal >= other_loop_side.flowRequestFinal)) {
1099 : // okay, just use basic logic
1100 67063 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1101 59564 : } else if (other_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal <= other_loop_side.flowRequestFinal)) {
1102 : // okay, just use basic logic
1103 13455 : LoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1104 : } else { // not okay, we have a case that will likely need special correcting
1105 : // 2. determine which loop side has the stepped data
1106 46109 : DataPlant::LoopSideLocation LoopSideIndex = DataPlant::LoopSideLocation::Invalid;
1107 46109 : if (this_loop_side.hasConstSpeedBranchPumps && (this_loop_side.flowRequestFinal < other_loop_side.flowRequestFinal)) {
1108 33485 : LoopSideIndex = this->plantLoc.loopSideNum;
1109 12624 : } else if (other_loop_side.hasConstSpeedBranchPumps && (other_loop_side.flowRequestFinal < this_loop_side.flowRequestFinal)) {
1110 12624 : LoopSideIndex = OtherSide;
1111 : }
1112 46109 : auto &loop_side(loop.LoopSide(LoopSideIndex));
1113 :
1114 : // 3. step through and find out needed information
1115 : // 3a. search the loop side with branch pumps and find the steps available with non-zero Myloads
1116 : // 3b. search the loop side with branch pumps and find the steps available with zero Myloads
1117 : // LoadedConstantSpeedBranchFlowRateSteps = 0.0;
1118 46109 : Real64 LoadedConstantSpeedBranchFlowRateSteps_sum = 0.0;
1119 46109 : this_loop_side.noLoadConstantSpeedBranchFlowRateSteps = 0.0;
1120 46109 : Real64 NoLoadConstantSpeedBranchFlowRateSteps_sum = 0.0;
1121 46109 : int ParallelBranchIndex = 0;
1122 46109 : int const NumBranchesOnThisLoopSide = loop_side.TotalBranches;
1123 46109 : auto const &loop_branches(loop_side.Branch);
1124 276654 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
1125 230545 : auto const &loop_branch(loop_branches(BranchCounter));
1126 230545 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) ++ParallelBranchIndex;
1127 230545 : if (loop_branch.HasConstantSpeedBranchPump) {
1128 92218 : Real64 const branch_mass_flow(loop_branch.ConstantSpeedBranchMassFlow);
1129 92218 : if (loop_branch.max_abs_Comp_MyLoad() > HVAC::SmallLoad) {
1130 20132 : LoadedConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
1131 : } else {
1132 72086 : this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex) = branch_mass_flow;
1133 72086 : NoLoadConstantSpeedBranchFlowRateSteps_sum += branch_mass_flow;
1134 : }
1135 : }
1136 : }
1137 :
1138 : // 4. allocate which branches to use,
1139 46109 : Real64 tmpLoopFlow = max(this_loop_side.flowRequestFinal, other_loop_side.flowRequestFinal);
1140 46109 : Real64 MaxBranchPumpLoopSideFlow = LoadedConstantSpeedBranchFlowRateSteps_sum + NoLoadConstantSpeedBranchFlowRateSteps_sum;
1141 46109 : tmpLoopFlow = min(tmpLoopFlow, MaxBranchPumpLoopSideFlow);
1142 : // 4b. first use all the branches with non-zero MyLoad
1143 46109 : if (tmpLoopFlow > LoadedConstantSpeedBranchFlowRateSteps_sum) {
1144 46109 : Real64 AccumFlowSteps = LoadedConstantSpeedBranchFlowRateSteps_sum;
1145 46109 : ParallelBranchIndex = 0;
1146 138327 : for (int BranchCounter = 1; BranchCounter <= NumBranchesOnThisLoopSide; ++BranchCounter) {
1147 138327 : if (BranchCounter > 1 && BranchCounter < NumBranchesOnThisLoopSide) {
1148 92218 : ++ParallelBranchIndex;
1149 : } else {
1150 46109 : continue;
1151 : }
1152 92218 : Real64 const steps = this_loop_side.noLoadConstantSpeedBranchFlowRateSteps(ParallelBranchIndex);
1153 92218 : if (steps > 0.0) { // add in branches with zero MyLoad in branch input order until satisfied
1154 72086 : if (tmpLoopFlow > AccumFlowSteps) {
1155 72086 : if (tmpLoopFlow <= AccumFlowSteps + steps) { // found it set requests and exit
1156 46109 : tmpLoopFlow = AccumFlowSteps + steps;
1157 46109 : loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
1158 46109 : LoopFlow = tmpLoopFlow;
1159 46109 : break;
1160 : } else {
1161 25977 : AccumFlowSteps += steps;
1162 25977 : loop_side.Branch(BranchCounter).RequestedMassFlow = steps;
1163 : }
1164 : }
1165 : }
1166 : }
1167 : }
1168 : }
1169 : }
1170 697676 : } else if (loop.CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
1171 433748 : LoopFlow = this_loop_side.flowRequestFinal;
1172 263928 : } else if (loop.CommonPipeType == DataPlant::CommonPipeType::Single) {
1173 263928 : LoopFlow = this_loop_side.flowRequestFinal;
1174 : }
1175 :
1176 : // overrides the loop solver flow request to allow loop pump to turn off when not in use
1177 36285941 : if (this_loop_side.TotalPumps == 1) {
1178 18345984 : if (LoopFlow < HVAC::VerySmallMassFlow) { // Update from dataconvergetols...
1179 44288586 : for (int BranchCounter = 1; BranchCounter <= this_loop_side.TotalBranches; ++BranchCounter) {
1180 : // reference
1181 35891604 : auto &branch(this_loop_side.Branch(BranchCounter));
1182 35891604 : int const NumCompsOnThisBranch = branch.TotalComponents;
1183 72259835 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
1184 36368231 : auto const &component(branch.Comp(CompCounter));
1185 36368231 : switch (component.Type) {
1186 7203455 : case DataPlant::PlantEquipmentType::PumpVariableSpeed:
1187 : case DataPlant::PlantEquipmentType::PumpBankVariableSpeed:
1188 : case DataPlant::PlantEquipmentType::PumpCondensate:
1189 7203455 : if (component.CompNum > 0) {
1190 7203455 : auto &this_pump(state.dataPumps->PumpEquip(component.CompNum));
1191 7203455 : this_pump.LoopSolverOverwriteFlag = true;
1192 : }
1193 : default:
1194 36368231 : break;
1195 : }
1196 : }
1197 : }
1198 : }
1199 : }
1200 :
1201 36285941 : return LoopFlow;
1202 : }
1203 :
1204 36285941 : void HalfLoopData::DoFlowAndLoadSolutionPass(EnergyPlusData &state, LoopSideLocation OtherSide, int ThisSideInletNode, bool FirstHVACIteration)
1205 : {
1206 :
1207 : // This is passed in-out deep down into the depths where the load op manager calls EMS and EMS can shut down pumps
1208 36285941 : bool LoopShutDownFlag = false;
1209 :
1210 : // First thing is to setup mass flow request information
1211 36285941 : Real64 ThisLoopSideFlowRequest = this->SetupLoopFlowRequest(state, OtherSide);
1212 :
1213 : // Now we know what the loop would "like" to run at, let's see the pump
1214 : // operation range (min/max avail) to see whether it is possible this time around
1215 36285941 : Real64 ThisLoopSideFlow = this->DetermineLoopSideFlowRate(state, ThisSideInletNode, ThisLoopSideFlowRequest);
1216 :
1217 266928092 : for (auto &branch : this->Branch) {
1218 230642151 : branch.lastComponentSimulated = 0;
1219 : }
1220 :
1221 : // We also need to establish a baseline "other-side-based" loop demand based on this possible flow rate
1222 36285941 : this->InitialDemandToLoopSetPoint = this->CalcOtherSideDemand(state, ThisLoopSideFlow);
1223 36285941 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
1224 36285941 : this->LoadToLoopSetPointThatWasntMet = 0.0;
1225 :
1226 : // We now have a loop side flow request, along with inlet min/max avails.
1227 : // We can now make a first pass through the component simulation, requesting flow as necessary.
1228 : // Normal "supply side" components will set a mass flow rate on their outlet node to request flow,
1229 : // while "Demand side" components will set a a mass flow request on their inlet node to request flow.
1230 36285941 : this->FlowLock = DataPlant::FlowLock::Unlocked;
1231 36285941 : this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
1232 :
1233 : // discussion/comments about loop solver/flow resolver interaction
1234 : // At this point, the components have been simulated. They should have either:
1235 : // - logged a massflowrequest
1236 : // - or logged a MassFlowRate
1237 : // We need to decide what the components are going to do on FlowLock=0.
1238 : // If we want all control here at the solver level, the components just need to
1239 : // log their MassFlowRate on their outlet nodes, or some other mechanism.
1240 : // Then the loop solver can scan the branch and get the max, and this will be the requested
1241 : // flow rate for the branch.
1242 : // The loop solver will then set this as the branch outlet mass flow rate in preparation
1243 : // for the flow resolver.
1244 : // The loop solver may need to do something to the inlet/outlet branch, but I'm not sure yet.
1245 : // The following comment block is what I had already thought of, and it may still make sense.
1246 :
1247 : // Now that all the flow requests have been logged, we need to prepare them for the
1248 : // flow resolver. This will just take the requests and determine the desired flow
1249 : // request for that branch according to pump placement, pump type, and other component
1250 : // conditions. In many cases, this will just be to simply take the max request from
1251 : // the branch, which will already be within pumping limits for that flow path.
1252 : // We can then call the flow resolver to lock down branch inlet flow rates.
1253 :
1254 : // The flow resolver takes information such as requested flows and min/max available flows and
1255 : // sets the corrected flow on the inlet to each parallel branch
1256 36285941 : this->ResolveParallelFlows(state, ThisLoopSideFlow, FirstHVACIteration);
1257 :
1258 : // Re-Initialize variables for this next pass
1259 36285941 : this->InitialDemandToLoopSetPointSAVED = this->InitialDemandToLoopSetPoint;
1260 36285941 : this->CurrentAlterationsToDemand = 0.0;
1261 36285941 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint;
1262 :
1263 : // Now that flow rates have been resolved, we just need to set the flow lock status
1264 : // flag, and resimulate. During this simulation each component will still use the
1265 : // SetFlowRequest routine, but this routine will also set the outlet flow rate
1266 : // equal to the inlet flow rate, according to flowlock logic.
1267 36285941 : this->FlowLock = DataPlant::FlowLock::Locked;
1268 36285941 : this->SimulateAllLoopSideBranches(state, ThisLoopSideFlow, FirstHVACIteration, LoopShutDownFlag);
1269 36285941 : }
1270 :
1271 36285941 : void HalfLoopData::ResolveParallelFlows(EnergyPlusData &state,
1272 : Real64 const ThisLoopSideFlow, // [kg/s] total flow to be split
1273 : bool const FirstHVACIteration // TRUE if First HVAC iteration of Time step
1274 : )
1275 : {
1276 :
1277 : // SUBROUTINE INFORMATION:
1278 : // AUTHOR Brandon Anderson, Dan Fisher
1279 : // DATE WRITTEN October 1999
1280 : // MODIFIED May 2005 Sankaranarayanan K P, Rich Liesen
1281 : // RE-ENGINEERED Sept 2010 Dan Fisher, Brent Griffith for demand side update
1282 :
1283 : // PURPOSE OF THIS SUBROUTINE:
1284 : // This subroutine takes the overall loop side flow and distributes
1285 : // it among parallel branches. this is the main implementation of
1286 : // flow splitting for plant splitter/mixer
1287 :
1288 : // METHODOLOGY EMPLOYED:
1289 : // Flow through the branches is currently determined by
1290 : // the active component on the branch, as well as the
1291 : // order of the branches following the splitter.
1292 : // SimPlantEquipment is run first, and the active components
1293 : // request their flow. These flows are compared and a simple
1294 : // algorithm balances flow in the branches. The flow in these
1295 : // branches is then locked down, via MassFlowRateMaxAvail and MinAvail
1296 : // SimPlant Equipment is then run again in order to get correct
1297 : // properties. Finally, Max/MinAvail are reset for the next time step.
1298 :
1299 : // Using/Aliasing
1300 :
1301 : // SUBROUTINE PARAMETER DEFINITIONS:
1302 36285941 : int constexpr LoopSideSingleBranch(1); // For readability
1303 :
1304 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1305 : Real64 ActiveFlowRate; // The flow available when cycling through branches
1306 : Real64 PassiveFlowRate; // The flow available when cycling through branches
1307 : Real64 FracFlow; // The flow available when cycling through branches
1308 : Real64 ThisBranchRequestFrac; // The request ratio
1309 : Real64 totalMax; // The flow available when cycling through branches
1310 : Real64 FlowRemaining; // The flow available when cycling through branches
1311 : int LastNodeOnBranch; // intermediate value used for better readabilty
1312 : int FirstNodeOnBranch; // intermediate value used for better readabilty
1313 : Real64 BranchFlowReq;
1314 : Real64 BranchMinAvail;
1315 : Real64 BranchMaxAvail;
1316 : Real64 ParallelBranchMaxAvail;
1317 : Real64 ParallelBranchMinAvail;
1318 : Real64 TotParallelBranchFlowReq;
1319 : Real64 StartingFlowRate;
1320 : Real64 ThisBranchRequest;
1321 :
1322 : // If there is no splitter then there is no continuity to enforce.
1323 36285941 : if (!this->Splitter.Exists) {
1324 :
1325 : // If there's only one branch, then RETURN
1326 160858 : if (this->TotalBranches == 1) {
1327 : // The branch should just try to meet the request previously calculated. This should be good,
1328 : // just need to make sure that during FlowUnlocked, no one constrained Min/Max farther.
1329 : // This would have been propagated down the branch, so we can check the outlet node min/max avail for this.
1330 160858 : auto const &this_single_branch(this->Branch(LoopSideSingleBranch));
1331 160858 : LastNodeOnBranch = this_single_branch.NodeNumOut;
1332 160858 : FirstNodeOnBranch = this_single_branch.NodeNumIn;
1333 160858 : BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
1334 160858 : BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
1335 160858 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = min(max(ThisLoopSideFlow, BranchMinAvail), BranchMaxAvail);
1336 : // now with flow locked, this single branch will just ran at the specified flow rate, so we are done
1337 31763770 : return;
1338 : } else {
1339 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
1340 0 : ShowContinueError(state, "There are multiple branches, yet no splitter. This is an invalid configuration.");
1341 0 : ShowContinueError(state, "Add a set of connectors, use put components on a single branch.");
1342 0 : ShowFatalError(state, "Invalid plant topology causes program termination.");
1343 0 : return;
1344 : }
1345 : }
1346 :
1347 : // If a splitter/mixer combination exist on the loop
1348 36125083 : if (this->Splitter.Exists && this->Mixer.Exists) {
1349 :
1350 : // Zero out local variables
1351 36125083 : TotParallelBranchFlowReq = 0.0;
1352 36125083 : int NumSplitOutlets = this->Splitter.TotalOutletNodes;
1353 36125083 : if (NumSplitOutlets < 1) {
1354 0 : ShowSevereError(state, "Plant topology problem on \"" + this->loopSideDescription + "\"");
1355 0 : ShowContinueError(state, "Diagnostic error in PlantLoopSolver::ResolveParallelFlows.");
1356 0 : ShowContinueError(state, "Splitter improperly specified, no splitter outlets.");
1357 0 : ShowFatalError(state, "Invalid plant topology causes program termination.");
1358 : }
1359 :
1360 36125083 : int NumActiveBranches = 0;
1361 36125083 : ParallelBranchMaxAvail = 0.0;
1362 36125083 : ParallelBranchMinAvail = 0.0;
1363 194356210 : for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
1364 :
1365 158231127 : int BranchNum = this->Splitter.BranchNumOut(iBranch);
1366 158231127 : auto &this_branch(this->Branch(BranchNum));
1367 158231127 : int SplitterBranchOut = this->Splitter.BranchNumOut(iBranch);
1368 158231127 : auto const &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
1369 158231127 : LastNodeOnBranch = this_branch.NodeNumOut;
1370 158231127 : FirstNodeOnBranch = this_branch.NodeNumIn;
1371 158231127 : BranchFlowReq = this_branch.DetermineBranchFlowRequest(state);
1372 158231127 : this_branch.RequestedMassFlow = BranchFlowReq; // store this for later use in logic for remaining flow allocations
1373 : // now, if we are have branch pumps, here is the situation:
1374 : // constant speed pumps lock in a flow request on the inlet node
1375 : // variable speed pumps which have other components on the branch do not log a request themselves
1376 : // the DetermineBranchFlowRequest routine only looks at the branch inlet node
1377 : // for variable speed branch pumps then, this won't work because the branch will be requesting zero
1378 : // so let's adjust for this here to make sure these branches get good representation
1379 : // This comment above is not true, for series active branches, DetermineBranchFlowRequest does scan down the branch's
1380 : // components already, no need to loop over components
1381 158231127 : BranchMinAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMinAvail;
1382 158231127 : BranchMaxAvail = state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRateMaxAvail;
1383 : // !sum the branch flow requests to a total parallel branch flow request
1384 158231127 : bool activeBranch = this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active;
1385 158231127 : bool isSeriesActiveAndRequesting =
1386 158231127 : (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) && (BranchFlowReq > 0.0);
1387 158231127 : if (activeBranch || isSeriesActiveAndRequesting) { // revised logic for series active
1388 122730579 : TotParallelBranchFlowReq += BranchFlowReq;
1389 122730579 : ++NumActiveBranches;
1390 : }
1391 158231127 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = BranchFlowReq;
1392 158231127 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail = BranchMinAvail;
1393 158231127 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail = BranchMaxAvail;
1394 158231127 : ParallelBranchMaxAvail += BranchMaxAvail;
1395 158231127 : ParallelBranchMinAvail += BranchMinAvail;
1396 : }
1397 : // ! Find branch number and flow rates at splitter inlet
1398 36125083 : int SplitterBranchIn = this->Splitter.BranchNumIn;
1399 36125083 : int FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
1400 : // ! Find branch number and flow rates at mixer outlet
1401 36125083 : int MixerBranchOut = this->Mixer.BranchNumOut;
1402 36125083 : LastNodeOnBranch = this->Branch(MixerBranchOut).NodeNumOut;
1403 36125083 : int FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1404 :
1405 36125083 : auto &first_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchIn));
1406 36125083 : auto &last_branch_inlet_node(state.dataLoopNodes->Node(FirstNodeOnBranchOut));
1407 :
1408 : // Reset branch inlet node flow rates for the first and last branch on loop
1409 36125083 : first_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
1410 36125083 : last_branch_inlet_node.MassFlowRate = ThisLoopSideFlow;
1411 :
1412 : // Reset branch inlet node Min/MaxAvails for the first and last branch on loop
1413 36125083 : first_branch_inlet_node.MassFlowRateMaxAvail = min(first_branch_inlet_node.MassFlowRateMaxAvail, ParallelBranchMaxAvail);
1414 36125083 : first_branch_inlet_node.MassFlowRateMaxAvail =
1415 36125083 : min(first_branch_inlet_node.MassFlowRateMaxAvail, last_branch_inlet_node.MassFlowRateMaxAvail);
1416 36125083 : first_branch_inlet_node.MassFlowRateMinAvail = max(first_branch_inlet_node.MassFlowRateMinAvail, ParallelBranchMinAvail);
1417 36125083 : first_branch_inlet_node.MassFlowRateMinAvail =
1418 36125083 : max(first_branch_inlet_node.MassFlowRateMinAvail, last_branch_inlet_node.MassFlowRateMinAvail);
1419 36125083 : last_branch_inlet_node.MassFlowRateMinAvail = first_branch_inlet_node.MassFlowRateMinAvail;
1420 36125083 : last_branch_inlet_node.MassFlowRateMaxAvail = first_branch_inlet_node.MassFlowRateMaxAvail;
1421 :
1422 : // Initialize the remaining flow variable
1423 36125083 : FlowRemaining = ThisLoopSideFlow;
1424 :
1425 : // Initialize flow on passive, bypass and uncontrolled parallel branches to zero. For these branches
1426 : // MinAvail is not enforced
1427 194356210 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1428 158231127 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1429 158231127 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1430 194126548 : if (this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::Active &&
1431 35895421 : this->Branch(SplitterBranchOut).controlType != DataBranchAirLoopPlant::ControlType::SeriesActive) {
1432 34977352 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
1433 69954704 : this->PushBranchFlowCharacteristics(
1434 34977352 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1435 : }
1436 : }
1437 :
1438 : // IF SUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
1439 36125083 : if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) { // no flow available at all for splitter
1440 86735032 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1441 69815470 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1442 140043143 : for (int CompCounter = 1; CompCounter <= this->Branch(SplitterBranchOut).TotalComponents; ++CompCounter) {
1443 :
1444 70227673 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1445 70227673 : int CompInletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumIn;
1446 70227673 : int CompOutletNode = this->Branch(SplitterBranchOut).Comp(CompCounter).NodeNumOut;
1447 70227673 : state.dataLoopNodes->Node(CompInletNode).MassFlowRate = 0.0;
1448 70227673 : state.dataLoopNodes->Node(CompInletNode).MassFlowRateMaxAvail = 0.0;
1449 70227673 : state.dataLoopNodes->Node(CompOutletNode).MassFlowRate = 0.0;
1450 70227673 : state.dataLoopNodes->Node(CompOutletNode).MassFlowRateMaxAvail = 0.0;
1451 : }
1452 : }
1453 16919562 : return;
1454 19205521 : } else if (FlowRemaining >= TotParallelBranchFlowReq) {
1455 :
1456 : // 1) Satisfy flow demand of ACTIVE splitter outlet branches
1457 84895042 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1458 70211692 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1459 70211692 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1460 84808120 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
1461 14596428 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
1462 : // branch flow is min of requested flow and remaining flow
1463 56012802 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1464 56012802 : min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FlowRemaining);
1465 56012802 : if (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance)
1466 12477042 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = 0.0;
1467 112025604 : this->PushBranchFlowCharacteristics(
1468 56012802 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1469 56012802 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1470 56012802 : if (FlowRemaining < DataBranchAirLoopPlant::MassFlowTolerance) FlowRemaining = 0.0;
1471 : }
1472 : }
1473 : // IF the active branches take the entire loop flow, return
1474 14683350 : if (FlowRemaining == 0.0) return;
1475 :
1476 : // 2) Distribute remaining flow to PASSIVE branches
1477 4212550 : totalMax = 0.0;
1478 26955151 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1479 22742601 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1480 22742601 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1481 22742601 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
1482 : // Calculate the total max available
1483 0 : totalMax += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
1484 : }
1485 : }
1486 :
1487 4212550 : if (totalMax > 0) {
1488 0 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1489 0 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1490 0 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1491 0 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Passive) {
1492 0 : FracFlow = FlowRemaining / totalMax;
1493 0 : if (FracFlow <= 1.0) { // the passive branches will take all the flow
1494 0 : PassiveFlowRate = FracFlow * state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail;
1495 : // Check against FlowRemaining
1496 0 : PassiveFlowRate = min(FlowRemaining, PassiveFlowRate);
1497 : // Allow FlowRequest to be increased to meet minimum on branch
1498 0 : PassiveFlowRate = max(PassiveFlowRate, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMinAvail);
1499 0 : FlowRemaining = max((FlowRemaining - PassiveFlowRate), 0.0);
1500 0 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = PassiveFlowRate;
1501 : } else { // Each Branch receives maximum flow and BYPASS must be used
1502 0 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1503 0 : min(state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail, FlowRemaining);
1504 0 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1505 : }
1506 0 : this->PushBranchFlowCharacteristics(
1507 0 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1508 : }
1509 : }
1510 : } // totalMax <=0 and flow should be assigned to active branches
1511 : // IF the passive branches take the remaining loop flow, return
1512 4212550 : if (FlowRemaining == 0.0) return;
1513 :
1514 : // 3) Distribute remaining flow to the BYPASS
1515 26955151 : for (int OutletNum = 1; OutletNum <= this->Splitter.TotalOutletNodes; ++OutletNum) {
1516 22742601 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1517 22742601 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1518 22742601 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Bypass) {
1519 3975987 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1520 3975987 : min(FlowRemaining, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
1521 7951974 : this->PushBranchFlowCharacteristics(
1522 3975987 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1523 3975987 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1524 : }
1525 : }
1526 : // IF the bypass take the remaining loop flow, return
1527 4212550 : if (FlowRemaining == 0.0) return;
1528 :
1529 : // 4) If PASSIVE branches and BYPASS are at max and there's still flow, distribute remaining flow to ACTIVE branches but only those
1530 : // that had a non-zero flow request. Try to leave branches off that wanted to be off.
1531 243793 : if (NumActiveBranches > 0) {
1532 196191 : ActiveFlowRate = FlowRemaining / NumActiveBranches; // denominator now only includes active branches that wanted to be "on"
1533 279720 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1534 262769 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1535 262769 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1536 262769 : bool branchIsActive = this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active;
1537 : bool branchIsSeriesActiveAndRequesting =
1538 280966 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive &&
1539 18197 : this->Branch(SplitterBranchOut).RequestedMassFlow > 0.0;
1540 262769 : if (branchIsActive || branchIsSeriesActiveAndRequesting) { // only series active branches that want to be "on"
1541 : // check Remaining flow (should be correct!)
1542 260967 : ActiveFlowRate = min(ActiveFlowRate, FlowRemaining);
1543 : // set the flow rate to the MIN((MassFlowRate+AvtiveFlowRate), MaxAvail)
1544 260967 : StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1545 260967 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate =
1546 260967 : min((state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate + ActiveFlowRate),
1547 260967 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail);
1548 521934 : this->PushBranchFlowCharacteristics(
1549 260967 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1550 : // adjust the remaining flow
1551 260967 : FlowRemaining -= (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate - StartingFlowRate);
1552 : }
1553 262769 : if (FlowRemaining == 0) break;
1554 : }
1555 : // IF the active branches take the remaining loop flow, return
1556 196191 : if (FlowRemaining == 0.0) return;
1557 :
1558 : // 5) Step 4) could have left ACTIVE branches < MaxAvail. Check to makes sure all ACTIVE branches are at MaxAvail
1559 56839 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1560 39888 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1561 39888 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1562 44260 : if (this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::Active ||
1563 4372 : this->Branch(SplitterBranchOut).controlType == DataBranchAirLoopPlant::ControlType::SeriesActive) {
1564 38988 : StartingFlowRate = state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1565 : ActiveFlowRate =
1566 38988 : min(FlowRemaining, (state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRateMaxAvail - StartingFlowRate));
1567 38988 : FlowRemaining -= ActiveFlowRate;
1568 38988 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = StartingFlowRate + ActiveFlowRate;
1569 77976 : this->PushBranchFlowCharacteristics(
1570 38988 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1571 : }
1572 : }
1573 : }
1574 : // IF the active branches take the remaining loop flow, return
1575 64553 : if (FlowRemaining == 0.0) return;
1576 :
1577 : // 6) Adjust Inlet branch and outlet branch flow rates to match parallel branch rate
1578 56600 : TotParallelBranchFlowReq = 0.0;
1579 120404 : for (int iBranch = 1; iBranch <= NumSplitOutlets; ++iBranch) {
1580 63804 : int BranchNum = this->Splitter.BranchNumOut(iBranch);
1581 63804 : FirstNodeOnBranch = this->Branch(BranchNum).NodeNumIn;
1582 : // calculate parallel branch flow rate
1583 63804 : TotParallelBranchFlowReq += state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1584 : }
1585 : // Reset the flow on the splitter inlet branch
1586 56600 : SplitterBranchIn = this->Splitter.BranchNumIn;
1587 56600 : FirstNodeOnBranchIn = this->Branch(SplitterBranchIn).NodeNumIn;
1588 56600 : state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate = TotParallelBranchFlowReq;
1589 113200 : this->PushBranchFlowCharacteristics(
1590 56600 : state, SplitterBranchIn, state.dataLoopNodes->Node(FirstNodeOnBranchIn).MassFlowRate, FirstHVACIteration);
1591 : // Reset the flow on the Mixer outlet branch
1592 56600 : MixerBranchOut = this->Mixer.BranchNumOut;
1593 56600 : FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1594 56600 : state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
1595 113200 : this->PushBranchFlowCharacteristics(
1596 56600 : state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
1597 56600 : return;
1598 :
1599 : // IF INSUFFICIENT FLOW TO MEET ALL PARALLEL BRANCH FLOW REQUESTS
1600 : } else {
1601 :
1602 : // 1) apportion flow based on requested fraction of total
1603 22726136 : for (int OutletNum = 1; OutletNum <= NumSplitOutlets; ++OutletNum) {
1604 :
1605 18203965 : int SplitterBranchOut = this->Splitter.BranchNumOut(OutletNum);
1606 18203965 : ThisBranchRequest = this->Branch(SplitterBranchOut).DetermineBranchFlowRequest(state);
1607 18203965 : FirstNodeOnBranch = this->Branch(SplitterBranchOut).NodeNumIn;
1608 18203965 : auto &this_splitter_outlet_branch(this->Branch(SplitterBranchOut));
1609 :
1610 18203965 : if ((this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::Active) ||
1611 4374288 : (this_splitter_outlet_branch.controlType == DataBranchAirLoopPlant::ControlType::SeriesActive)) {
1612 :
1613 : // since we are calculating this fraction based on the total parallel request calculated above, we must mimic the logic to
1614 : // make sure the math works every time that means we must make the variable speed pump correction here as well.
1615 28093322 : for (int CompCounter = 1; CompCounter <= this_splitter_outlet_branch.TotalComponents; ++CompCounter) {
1616 :
1617 14118989 : auto const &this_comp(this_splitter_outlet_branch.Comp(CompCounter));
1618 :
1619 : // if this isn't a variable speed pump then just keep cycling
1620 14118989 : if ((this_comp.Type != PlantEquipmentType::PumpVariableSpeed) &&
1621 14117347 : (this_comp.Type != PlantEquipmentType::PumpBankVariableSpeed)) {
1622 14117347 : continue;
1623 : }
1624 :
1625 1642 : int CompInletNode = this_comp.NodeNumIn;
1626 1642 : ThisBranchRequest = max(ThisBranchRequest, state.dataLoopNodes->Node(CompInletNode).MassFlowRateRequest);
1627 : }
1628 :
1629 13974333 : ThisBranchRequestFrac = ThisBranchRequest / TotParallelBranchFlowReq;
1630 : // FracFlow = state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate/TotParallelBranchFlowReq
1631 : // state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate = MIN((FracFlow *
1632 : // state.dataLoopNodes->Node(FirstNodeOnBranch)%MassFlowRate),FlowRemaining)
1633 13974333 : state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate = ThisBranchRequestFrac * ThisLoopSideFlow;
1634 27948666 : this->PushBranchFlowCharacteristics(
1635 13974333 : state, SplitterBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate, FirstHVACIteration);
1636 13974333 : FlowRemaining -= state.dataLoopNodes->Node(FirstNodeOnBranch).MassFlowRate;
1637 : }
1638 : }
1639 :
1640 : // 1b) check if flow all apportioned
1641 4522171 : if (FlowRemaining > DataBranchAirLoopPlant::MassFlowTolerance) {
1642 : // Call fatal diagnostic error. !The math should work out!
1643 0 : ShowSevereError(state, "ResolveParallelFlows: Dev note, failed to redistribute restricted flow");
1644 0 : ShowContinueErrorTimeStamp(state, "");
1645 0 : ShowContinueError(state, format("Loop side flow = {:.8R} (kg/s)", ThisLoopSideFlow));
1646 0 : ShowContinueError(state, format("Flow Remaining = {:.8R} (kg/s)", FlowRemaining));
1647 0 : ShowContinueError(state, format("Parallel Branch requests = {:.8R} (kg/s)", TotParallelBranchFlowReq));
1648 : }
1649 :
1650 : // 2) ! Reset the flow on the Mixer outlet branch
1651 4522171 : MixerBranchOut = this->Mixer.BranchNumOut;
1652 4522171 : FirstNodeOnBranchOut = this->Branch(MixerBranchOut).NodeNumIn;
1653 4522171 : state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate = TotParallelBranchFlowReq;
1654 9044342 : this->PushBranchFlowCharacteristics(
1655 4522171 : state, MixerBranchOut, state.dataLoopNodes->Node(FirstNodeOnBranchOut).MassFlowRate, FirstHVACIteration);
1656 :
1657 : } // Total flow requested >= or < Total parallel request
1658 :
1659 : } // Splitter/Mixer exists
1660 : }
1661 :
1662 217072214 : void HalfLoopData::SimulateLoopSideBranchGroup(EnergyPlusData &state,
1663 : int const FirstBranchNum,
1664 : int const LastBranchNum,
1665 : Real64 FlowRequest,
1666 : bool const FirstHVACIteration,
1667 : bool &LoopShutDownFlag)
1668 : {
1669 :
1670 : // SUBROUTINE INFORMATION:
1671 : // AUTHOR Edwin Lee
1672 : // DATE WRITTEN July 2010
1673 : // MODIFIED na
1674 : // RE-ENGINEERED na
1675 :
1676 : // PURPOSE OF THIS SUBROUTINE:
1677 : // This routine will manage the component simulation on a single set of parallel branches
1678 : // This routine also reverts to a single branch simulation if there isn't a set of parallel branches
1679 :
1680 : // METHODOLOGY EMPLOYED:
1681 : // Loop through all components, and simulate first the non-load range based on each branch.
1682 : // When a load-range based (LRB) is encountered, the simulation moves to the next branch to do non-LRB components.
1683 : // When all paths are exhausted the simulation begins simulating LRB components. Before each comp, the load distribution
1684 : // engine is called to handle the load distribution for this current pass. If load is successfully distributed, this is
1685 : // flagged, and not called again. If load is not distributed (i.e. this component isn't ON right now), then the
1686 : // load distribution engine will be called again before the next component.
1687 : // After all load distribution is done and those components are complete, the simulation moves back to do any
1688 : // remaining components that may be downstream.
1689 :
1690 : //~ Flags
1691 : bool LoadDistributionWasPerformed;
1692 :
1693 : //~ General variables
1694 : Real64 LoadToLoopSetPoint;
1695 217072214 : PlantLocation PumpLocation;
1696 217072214 : LoadToLoopSetPoint = 0.0;
1697 :
1698 : // We now know what plant simulation region is available to us, let's simulate this group
1699 217072214 : bool EncounteredLRBObjDuringPass1(false);
1700 678356516 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1701 461284302 : auto &branch(this->Branch(BranchCounter));
1702 :
1703 : //~ Always start from the last component we did the last time around + 1 and
1704 : //~ try to make it all the way to the end of the loop
1705 461284302 : int const StartingComponent = branch.lastComponentSimulated + 1;
1706 461284302 : int const EndingComponent = branch.TotalComponents;
1707 886456706 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1708 :
1709 463821104 : auto &this_comp(branch.Comp(CompCounter));
1710 463821104 : PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
1711 463821104 : DataPlant::OpScheme const CurOpSchemeType(this_comp.CurOpSchemeType);
1712 :
1713 463821104 : switch (CurOpSchemeType) {
1714 0 : case DataPlant::OpScheme::WSEcon: //~ coils
1715 0 : this_comp.MyLoad = UpdatedDemandToLoopSetPoint;
1716 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1717 0 : break;
1718 37404504 : case DataPlant::OpScheme::Pump: //~ pump
1719 37404504 : if (this->BranchPumpsExist) {
1720 712536 : SimulateSinglePump(state, this_comp.location, branch.RequestedMassFlow);
1721 : } else {
1722 36691968 : SimulateSinglePump(state, this_comp.location, FlowRequest);
1723 : }
1724 37404504 : break;
1725 1160704 : case DataPlant::OpScheme::CompSetPtBased:
1726 1160704 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1727 : this_plantLoc,
1728 : LoadToLoopSetPoint,
1729 1160704 : LoadToLoopSetPointThatWasntMet,
1730 : FirstHVACIteration,
1731 : LoopShutDownFlag,
1732 : LoadDistributionWasPerformed);
1733 1160704 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1734 1160704 : break;
1735 322816 : case DataPlant::OpScheme::EMS:
1736 322816 : if (this->plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
1737 322816 : int const curCompOpSchemePtr = this_comp.CurCompLevelOpNum;
1738 322816 : int const OpSchemePtr = this_comp.OpScheme(curCompOpSchemePtr).OpSchemePtr;
1739 322816 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).OpScheme(OpSchemePtr).EMSIntVarLoopDemandRate = InitialDemandToLoopSetPoint;
1740 : }
1741 322816 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1742 : this_plantLoc,
1743 322816 : UpdatedDemandToLoopSetPoint,
1744 322816 : LoadToLoopSetPointThatWasntMet,
1745 : FirstHVACIteration,
1746 : LoopShutDownFlag,
1747 : LoadDistributionWasPerformed);
1748 322816 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1749 322816 : break;
1750 38648700 : case OpScheme::WetBulbRB:
1751 : case OpScheme::DryBulbRB:
1752 : case OpScheme::DewPointRB:
1753 : case OpScheme::RelHumRB:
1754 : case OpScheme::DryBulbTDB:
1755 : case OpScheme::WetBulbTDB:
1756 : case OpScheme::DewPointTDB:
1757 : case OpScheme::HeatingRB:
1758 : case OpScheme::CoolingRB: { //~ load range based
1759 38648700 : EncounteredLRBObjDuringPass1 = true;
1760 38648700 : goto components_end; // don't do any more components on this branch
1761 : }
1762 386284380 : default: // demand, etc.
1763 386284380 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1764 : }
1765 :
1766 : // Update loop demand as needed for changes this component may have made
1767 425172404 : this->UpdateAnyLoopDemandAlterations(state, BranchCounter, CompCounter);
1768 :
1769 : //~ If we didn't EXIT early, we must have simulated, so update array
1770 425172404 : branch.lastComponentSimulated = CompCounter;
1771 :
1772 : } //~ CompCounter
1773 422635602 : components_end:;
1774 :
1775 461284302 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1776 230642151 : PlantPressureSystem::SimPressureDropSystem(
1777 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1778 : }
1779 :
1780 : } //~ BranchCounter
1781 :
1782 : // So now we have made one pass through all of the available components on these branches, skipping load based
1783 : // If we didn't encounter any load based objects during the first pass, then we must be done!
1784 251052652 : if (!EncounteredLRBObjDuringPass1) return;
1785 :
1786 : // If we have load based now, we should go ahead and distribute the load
1787 : // If not then this branch group is done, since flow path validation was previously done
1788 33980438 : LoadToLoopSetPoint = UpdatedDemandToLoopSetPoint;
1789 33980438 : LoadDistributionWasPerformed = false;
1790 :
1791 : // The way the load distribution is set up, I think I should call this for every load range based component
1792 : // encountered until distribution is actually performed. If we don't call for each component then we may
1793 : // call for a component that is not on the current equip list and then nothing would come on.
1794 33980438 : bool EncounteredNonLBObjDuringPass2(false);
1795 108341366 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1796 74360928 : auto &branch(this->Branch(BranchCounter));
1797 :
1798 : //~ Always start from the last component we did the last time around + 1 and
1799 : //~ try to make it all the way to the end of the loop
1800 74360928 : int const StartingComponent = branch.lastComponentSimulated + 1;
1801 74360928 : int const EndingComponent = branch.TotalComponents;
1802 113775648 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1803 39414720 : PlantLocation this_plantLoc = {this->plantLoc.loopNum, this->plantLoc.loopSideNum, BranchCounter, CompCounter};
1804 :
1805 39414720 : DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
1806 :
1807 39414720 : switch (CurOpSchemeType) {
1808 129108 : case DataPlant::OpScheme::NoControl: //~ pipes, for example
1809 129108 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1810 129108 : break;
1811 0 : case DataPlant::OpScheme::Demand:
1812 : case DataPlant::OpScheme::CompSetPtBased:
1813 : case DataPlant::OpScheme::FreeRejection: //~ other control types
1814 0 : EncounteredNonLBObjDuringPass2 = true;
1815 0 : goto components2_end; // don't do anymore components on this branch
1816 0 : case DataPlant::OpScheme::Pump: //~ pump
1817 0 : PumpLocation.loopNum = this->plantLoc.loopNum;
1818 0 : PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
1819 0 : PumpLocation.branchNum = BranchCounter;
1820 0 : PumpLocation.compNum = CompCounter;
1821 0 : if (this->BranchPumpsExist) {
1822 0 : SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
1823 : } else {
1824 0 : SimulateSinglePump(state, PumpLocation, FlowRequest);
1825 : }
1826 0 : break;
1827 39285612 : case OpScheme::WetBulbRB:
1828 : case OpScheme::DryBulbRB:
1829 : case OpScheme::DewPointRB:
1830 : case OpScheme::RelHumRB:
1831 : case OpScheme::DryBulbTDB:
1832 : case OpScheme::WetBulbTDB:
1833 : case OpScheme::DewPointTDB:
1834 : case OpScheme::HeatingRB:
1835 : case OpScheme::CoolingRB: { //~ load range based
1836 39285612 : if (!LoadDistributionWasPerformed) { //~ Still need to distribute load among load range based components
1837 37227184 : PlantCondLoopOperation::ManagePlantLoadDistribution(state,
1838 : this_plantLoc,
1839 : LoadToLoopSetPoint,
1840 37227184 : LoadToLoopSetPointThatWasntMet,
1841 : FirstHVACIteration,
1842 : LoopShutDownFlag,
1843 : LoadDistributionWasPerformed);
1844 : }
1845 39285612 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1846 39285612 : break;
1847 : }
1848 0 : default:
1849 0 : break;
1850 : }
1851 :
1852 : //~ If we didn't EXIT early, we must have simulated, so update array
1853 39414720 : branch.lastComponentSimulated = CompCounter;
1854 :
1855 : } //~ CompCounter
1856 74360928 : components2_end:;
1857 :
1858 : //~ If we are locked, go ahead and simulate the pressure components on this branch
1859 74360928 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1860 37180464 : PlantPressureSystem::SimPressureDropSystem(
1861 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1862 : }
1863 :
1864 : } //~ BranchCounter
1865 :
1866 : // So now we have made the load range based pass through all the components on each branch
1867 : // If we didn't see any other component types, then we are done, go away
1868 33980438 : if (!EncounteredNonLBObjDuringPass2) return;
1869 :
1870 : // If we did encounter other objects than we just need to go back through and simulate them
1871 0 : for (int BranchCounter = FirstBranchNum; BranchCounter <= LastBranchNum; ++BranchCounter) {
1872 0 : auto &branch(this->Branch(BranchCounter));
1873 :
1874 : //~ Always start from the last component we did the last time around + 1 and
1875 : //~ try to make it all the way to the end of the loop
1876 0 : int const StartingComponent = branch.lastComponentSimulated + 1;
1877 0 : int const EndingComponent = branch.TotalComponents;
1878 0 : for (int CompCounter = StartingComponent; CompCounter <= EndingComponent; ++CompCounter) {
1879 :
1880 0 : DataPlant::OpScheme const CurOpSchemeType(branch.Comp(CompCounter).CurOpSchemeType);
1881 :
1882 0 : switch (CurOpSchemeType) {
1883 0 : case DataPlant::OpScheme::Demand: //~ coils
1884 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1885 0 : break;
1886 0 : case DataPlant::OpScheme::Pump: //~ pump
1887 0 : PumpLocation.loopNum = this->plantLoc.loopNum;
1888 0 : PumpLocation.loopSideNum = this->plantLoc.loopSideNum;
1889 0 : PumpLocation.branchNum = BranchCounter;
1890 0 : PumpLocation.compNum = CompCounter;
1891 0 : if (this->BranchPumpsExist) {
1892 0 : SimulateSinglePump(state, PumpLocation, branch.RequestedMassFlow);
1893 : } else {
1894 0 : SimulateSinglePump(state, PumpLocation, FlowRequest);
1895 : }
1896 0 : break;
1897 0 : case OpScheme::HeatingRB:
1898 : case OpScheme::CoolingRB: { //~ load range based
1899 0 : ShowFatalError(state, "Encountered Load Based Object after other components, invalid.");
1900 0 : break;
1901 : }
1902 0 : default:
1903 : //~ Typical control equipment
1904 0 : branch.Comp(CompCounter).simulate(state, FirstHVACIteration);
1905 : }
1906 :
1907 : //~ If we didn't EXIT early, we must have simulated, so update array
1908 0 : branch.lastComponentSimulated = CompCounter;
1909 :
1910 : } //~ CompCounter
1911 :
1912 0 : if (this->FlowLock == DataPlant::FlowLock::Locked) {
1913 0 : PlantPressureSystem::SimPressureDropSystem(
1914 : state, this->plantLoc.loopNum, FirstHVACIteration, DataPlant::PressureCall::Calc, this->plantLoc.loopSideNum, BranchCounter);
1915 : }
1916 :
1917 : } //~ BranchCounter
1918 :
1919 : // I suppose I could do a check on the last component simulated to make sure we actually exhausted all branches
1920 : // This would be the "THIRD" check on flow validation, but would be OK
1921 : }
1922 :
1923 425172404 : void HalfLoopData::UpdateAnyLoopDemandAlterations(EnergyPlusData &state, int const BranchNum, int const CompNum)
1924 : {
1925 :
1926 : // SUBROUTINE INFORMATION:
1927 : // AUTHOR Edwin Lee
1928 : // DATE WRITTEN August 2010
1929 : // MODIFIED na
1930 : // RE-ENGINEERED na
1931 :
1932 : // PURPOSE OF THIS SUBROUTINE:
1933 : // This routine will analyze the given component and determine if any
1934 : // alterations need to be made to the current loop demand value. If so,
1935 : // it will make the changes to the module level loop demand variables.
1936 :
1937 : // METHODOLOGY EMPLOYED:
1938 : // Components will always supply a useful delta T, even if it happens to be zero
1939 : // For flow rate, make decisions based on the component's current operating scheme type:
1940 : // Demand based: these components will have a flow request on their inlet node
1941 : // Pump: these components will not be included, as they no longer include heat at the pump
1942 : // component setpoint: these components will have a flow request
1943 :
1944 : // on their outlet node corresponding to their calculated delta T
1945 : // load range based: these components do not 'alter' the load, they reject the load
1946 : // Therefore they are not included
1947 :
1948 : // SUBROUTINE PARAMETER DEFINITIONS:
1949 : static constexpr std::string_view RoutineName("PlantLoopSolver::UpdateAnyLoopDemandAlterations");
1950 :
1951 : // Init to zero, so that if we don't find anything, we exit early
1952 425172404 : Real64 ComponentMassFlowRate(0.0);
1953 :
1954 425172404 : auto const &this_comp(this->Branch(BranchNum).Comp(CompNum));
1955 :
1956 : // Get information
1957 425172404 : int const InletNode(this_comp.NodeNumIn);
1958 425172404 : int const OutletNode(this_comp.NodeNumOut);
1959 :
1960 425172404 : if (this->FlowLock == DataPlant::FlowLock::Unlocked) {
1961 :
1962 212586202 : switch (this_comp.CurOpSchemeType) {
1963 0 : case OpScheme::HeatingRB:
1964 : case OpScheme::CoolingRB: { //~ load range based
1965 0 : break; // Don't do anything for load based components
1966 : }
1967 :
1968 212586202 : default: {
1969 : // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
1970 212586202 : ComponentMassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
1971 : // make sure components like economizers use the mass flow request
1972 212586202 : break;
1973 : }
1974 : }
1975 :
1976 212586202 : } else if (this->FlowLock == DataPlant::FlowLock::Locked) {
1977 :
1978 : // For locked flow just use the mass flow rate
1979 :
1980 212586202 : switch (this_comp.CurOpSchemeType) {
1981 0 : case OpScheme::HeatingRB:
1982 : case OpScheme::CoolingRB: { //~ load range based
1983 0 : break; // Don't do anything for load based components
1984 : }
1985 212586202 : default: {
1986 : // pumps pipes, etc. will be lumped in here with other component types, but they will have no delta T anyway
1987 212586202 : ComponentMassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1988 : }
1989 : }
1990 :
1991 : } else { // flow pump query? problem?
1992 : }
1993 :
1994 : // Leave early if there wasn't a mass flow rate or request
1995 425172404 : if (ComponentMassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) return;
1996 :
1997 : // Get an average temperature for the property call
1998 149140286 : Real64 const InletTemp(state.dataLoopNodes->Node(InletNode).Temp);
1999 149140286 : Real64 const OutletTemp(state.dataLoopNodes->Node(OutletNode).Temp);
2000 149140286 : Real64 const AverageTemp((InletTemp + OutletTemp) / 2.0);
2001 149140286 : Real64 const ComponentCp(FluidProperties::GetSpecificHeatGlycol(state,
2002 149140286 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
2003 : AverageTemp,
2004 149140286 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
2005 : RoutineName));
2006 :
2007 : // Calculate the load altered by this component
2008 149140286 : Real64 const LoadAlteration(ComponentMassFlowRate * ComponentCp * (OutletTemp - InletTemp));
2009 :
2010 : // Now alter the module level variables
2011 149140286 : this->CurrentAlterationsToDemand += LoadAlteration;
2012 149140286 : this->UpdatedDemandToLoopSetPoint = this->InitialDemandToLoopSetPoint - this->CurrentAlterationsToDemand;
2013 : }
2014 :
2015 37404504 : void HalfLoopData::SimulateSinglePump(EnergyPlusData &state, PlantLocation const SpecificPumpLocation, Real64 &SpecificPumpFlowRate)
2016 : {
2017 :
2018 : // SUBROUTINE INFORMATION:
2019 : // AUTHOR Edwin Lee
2020 : // DATE WRITTEN July 2010
2021 : // MODIFIED na
2022 : // RE-ENGINEERED na
2023 :
2024 37404504 : auto &loop(state.dataPlnt->PlantLoop(SpecificPumpLocation.loopNum));
2025 37404504 : auto &loop_side(loop.LoopSide(SpecificPumpLocation.loopSideNum));
2026 37404504 : auto &loop_side_branch(loop_side.Branch(SpecificPumpLocation.branchNum));
2027 37404504 : auto &comp(loop_side_branch.Comp(SpecificPumpLocation.compNum));
2028 37404504 : int const PumpIndex = comp.IndexInLoopSidePumps;
2029 37404504 : auto &pump(loop_side.Pumps(PumpIndex));
2030 :
2031 37404504 : this->AdjustPumpFlowRequestByEMSControls(SpecificPumpLocation.branchNum, SpecificPumpLocation.compNum, SpecificPumpFlowRate);
2032 :
2033 : // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
2034 : bool DummyThisPumpRunning;
2035 37404504 : Pumps::SimPumps(state,
2036 37404504 : pump.PumpName,
2037 37404504 : SpecificPumpLocation.loopNum,
2038 : SpecificPumpFlowRate,
2039 : DummyThisPumpRunning,
2040 37404504 : loop_side_branch.PumpIndex,
2041 37404504 : pump.PumpHeatToFluid);
2042 :
2043 : //~ Pull some state information from the pump outlet node
2044 37404504 : pump.CurrentMinAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMinAvail;
2045 37404504 : pump.CurrentMaxAvail = state.dataLoopNodes->Node(pump.PumpOutletNode).MassFlowRateMaxAvail;
2046 :
2047 : //~ Update the LoopSide pump heat totality here
2048 37404504 : if (loop_side.TotalPumps > 0) {
2049 37404504 : loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
2050 : }
2051 37404504 : }
2052 :
2053 18526500 : void HalfLoopData::SimulateAllLoopSidePumps(EnergyPlusData &state,
2054 : ObjexxFCL::Optional<PlantLocation const> SpecificPumpLocation,
2055 : ObjexxFCL::Optional<Real64 const> SpecificPumpFlowRate)
2056 : {
2057 :
2058 : // SUBROUTINE INFORMATION:
2059 : // AUTHOR Edwin Lee
2060 : // DATE WRITTEN July 2010
2061 : // MODIFIED na
2062 : // RE-ENGINEERED na
2063 :
2064 : int PumpIndexStart;
2065 : int PumpIndexEnd;
2066 : int PumpLoopNum;
2067 : DataPlant::LoopSideLocation PumpLoopSideNum;
2068 :
2069 : // 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
2070 18526500 : if (present(SpecificPumpLocation)) {
2071 0 : PumpLoopNum = SpecificPumpLocation().loopNum;
2072 0 : PumpLoopSideNum = SpecificPumpLocation().loopSideNum;
2073 0 : int const PumpBranchNum = SpecificPumpLocation().branchNum;
2074 0 : int const PumpCompNum = SpecificPumpLocation().compNum;
2075 0 : PumpIndexStart =
2076 0 : state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum).Branch(PumpBranchNum).Comp(PumpCompNum).IndexInLoopSidePumps;
2077 0 : PumpIndexEnd = PumpIndexStart;
2078 : } else {
2079 18526500 : PumpLoopNum = this->plantLoc.loopNum;
2080 18526500 : PumpLoopSideNum = this->plantLoc.loopSideNum;
2081 18526500 : PumpIndexStart = 1;
2082 18526500 : PumpIndexEnd = this->TotalPumps;
2083 : }
2084 :
2085 : // If we have a flow rate to hit, then go for it, otherwise, just operate in request mode with zero flow
2086 : Real64 FlowToRequest;
2087 18526500 : if (present(SpecificPumpFlowRate)) {
2088 0 : FlowToRequest = SpecificPumpFlowRate;
2089 : } else {
2090 18526500 : FlowToRequest = 0.0;
2091 : }
2092 :
2093 : //~ Now loop through all the pumps and simulate them, keeping track of their status
2094 18526500 : auto &loop_side(state.dataPlnt->PlantLoop(PumpLoopNum).LoopSide(PumpLoopSideNum));
2095 18526500 : auto &loop_side_branch(loop_side.Branch);
2096 37229965 : for (int PumpCounter = PumpIndexStart; PumpCounter <= PumpIndexEnd; ++PumpCounter) {
2097 :
2098 : //~ Set some variables
2099 18703465 : auto &pump(loop_side.Pumps(PumpCounter));
2100 18703465 : int const PumpBranchNum = pump.BranchNum;
2101 18703465 : int const PumpCompNum = pump.CompNum;
2102 18703465 : int const PumpOutletNode = pump.PumpOutletNode;
2103 :
2104 18703465 : this->AdjustPumpFlowRequestByEMSControls(PumpBranchNum, PumpCompNum, FlowToRequest);
2105 :
2106 : // Call SimPumps, routine takes a flow request, and returns some info about the status of the pump
2107 : bool DummyThisPumpRunning;
2108 18703465 : Pumps::SimPumps(state,
2109 18703465 : pump.PumpName,
2110 : PumpLoopNum,
2111 : FlowToRequest,
2112 : DummyThisPumpRunning,
2113 18703465 : loop_side_branch(PumpBranchNum).PumpIndex,
2114 18703465 : pump.PumpHeatToFluid);
2115 :
2116 : //~ Pull some state information from the pump outlet node
2117 18703465 : Real64 const ThisPumpMinAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMinAvail;
2118 18703465 : Real64 const ThisPumpMaxAvail = state.dataLoopNodes->Node(PumpOutletNode).MassFlowRateMaxAvail;
2119 :
2120 : //~ Now update the data structure
2121 18703465 : pump.CurrentMinAvail = ThisPumpMinAvail;
2122 18703465 : pump.CurrentMaxAvail = ThisPumpMaxAvail;
2123 : }
2124 :
2125 : //~ Update the LoopSide pump heat totality here
2126 18526500 : if (loop_side.TotalPumps > 0) {
2127 18525323 : loop_side.TotalPumpHeat = sum(loop_side.Pumps, &DataPlant::LoopSidePumpInformation::PumpHeatToFluid);
2128 : }
2129 18526500 : }
2130 :
2131 36285941 : Real64 HalfLoopData::DetermineLoopSideFlowRate(EnergyPlusData &state, int ThisSideInletNode, Real64 ThisSideLoopFlowRequest)
2132 : {
2133 36285941 : Real64 ThisLoopSideFlow = ThisSideLoopFlowRequest;
2134 36285941 : Real64 TotalPumpMinAvailFlow = 0.0;
2135 36285941 : Real64 TotalPumpMaxAvailFlow = 0.0;
2136 36285941 : if (allocated(this->Pumps)) {
2137 :
2138 : //~ Initialize pump values
2139 37226370 : for (auto &e : this->Pumps) {
2140 18702252 : e.CurrentMinAvail = 0.0;
2141 18702252 : e.CurrentMaxAvail = 0.0;
2142 : }
2143 18524118 : this->FlowLock = DataPlant::FlowLock::PumpQuery;
2144 :
2145 : //~ Simulate pumps
2146 18524118 : this->SimulateAllLoopSidePumps(state);
2147 :
2148 : //~ Calculate totals
2149 37226370 : for (auto const &e : this->Pumps) {
2150 18702252 : TotalPumpMinAvailFlow += e.CurrentMinAvail;
2151 18702252 : TotalPumpMaxAvailFlow += e.CurrentMaxAvail;
2152 : }
2153 :
2154 : // Use the pump min/max avail to attempt to constrain the loop side flow
2155 18524118 : ThisLoopSideFlow = PlantUtilities::BoundValueToWithinTwoValues(ThisLoopSideFlow, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
2156 : }
2157 :
2158 : // Now we check flow restriction from the other side, both min and max avail.
2159 : // Doing this last basically means it wins, so the pump should pull down to meet the flow restriction
2160 36285941 : ThisLoopSideFlow = PlantUtilities::BoundValueToNodeMinMaxAvail(state, ThisLoopSideFlow, ThisSideInletNode);
2161 :
2162 : // Final preparation of loop inlet min/max avail if pumps exist
2163 36285941 : if (allocated(this->Pumps)) {
2164 : // At this point, the pump limits should have been obeyed unless a flow restriction was encountered from the other side
2165 : // The pump may, however, have even tighter constraints than the other side
2166 : // At this point, the inlet node doesn't know anything about those limits
2167 : // Since we have already honored the other side flow restriction, try to honor the pump limits here
2168 18524118 : PlantUtilities::TightenNodeMinMaxAvails(state, ThisSideInletNode, TotalPumpMinAvailFlow, TotalPumpMaxAvailFlow);
2169 : }
2170 :
2171 : // Now reset the entering mass flow rate to the decided-upon flow rate
2172 36285941 : state.dataLoopNodes->Node(ThisSideInletNode).MassFlowRate = ThisLoopSideFlow;
2173 36285941 : return ThisLoopSideFlow;
2174 : }
2175 :
2176 72250166 : void HalfLoopData::UpdatePlantMixer(EnergyPlusData &state)
2177 : {
2178 :
2179 : // SUBROUTINE INFORMATION:
2180 : // AUTHOR Brandon Anderson, Dan Fisher
2181 : // DATE WRITTEN October 1999
2182 : // MODIFIED na
2183 : // RE-ENGINEERED na
2184 :
2185 : // PURPOSE OF THIS SUBROUTINE:
2186 : // calculate the outlet conditions at the mixer
2187 : // this is expected to only be called for loops with a mixer
2188 :
2189 : // Find mixer outlet node number
2190 72250166 : int const MixerOutletNode = this->Mixer.NodeNumOut;
2191 :
2192 : // Find corresponding splitter inlet node number--correspondence, but currently
2193 : // hard code things to a single split/mix setting it to the mixer number
2194 72250166 : int const SplitterInNode = this->Splitter.NodeNumIn;
2195 : // Initialize Mixer outlet temp and mass flow rate
2196 72250166 : Real64 MixerOutletTemp = 0.0;
2197 72250166 : Real64 MixerOutletMassFlow = 0.0;
2198 72250166 : Real64 MixerOutletMassFlowMaxAvail = 0.0;
2199 72250166 : Real64 MixerOutletMassFlowMinAvail = 0.0;
2200 72250166 : Real64 MixerOutletPress = 0.0;
2201 72250166 : Real64 MixerOutletQuality = 0.0;
2202 :
2203 : // Calculate Mixer outlet mass flow rate
2204 388712420 : for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
2205 316462254 : int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
2206 316462254 : MixerOutletMassFlow += state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
2207 : }
2208 :
2209 : // Calculate Mixer outlet temperature
2210 252485644 : for (int InletNodeNum = 1; InletNodeNum <= this->Mixer.TotalInletNodes; ++InletNodeNum) {
2211 213702690 : int const MixerInletNode = this->Mixer.NodeNumIn(InletNodeNum);
2212 213702690 : if (MixerOutletMassFlow > 0.0) {
2213 180235478 : Real64 const MixerInletMassFlow = state.dataLoopNodes->Node(MixerInletNode).MassFlowRate;
2214 180235478 : Real64 const MassFrac = MixerInletMassFlow / MixerOutletMassFlow;
2215 : // mass flow weighted temp and enthalpy for each mixer inlet
2216 180235478 : MixerOutletTemp += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Temp;
2217 180235478 : MixerOutletQuality += MassFrac * state.dataLoopNodes->Node(MixerInletNode).Quality;
2218 180235478 : MixerOutletMassFlowMaxAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMaxAvail;
2219 180235478 : MixerOutletMassFlowMinAvail += state.dataLoopNodes->Node(MixerInletNode).MassFlowRateMinAvail;
2220 180235478 : MixerOutletPress = max(MixerOutletPress, state.dataLoopNodes->Node(MixerInletNode).Press);
2221 : } else { // MixerOutletMassFlow <=0, then perform the 'no flow' update.
2222 33467212 : MixerOutletTemp = state.dataLoopNodes->Node(SplitterInNode).Temp;
2223 33467212 : MixerOutletQuality = state.dataLoopNodes->Node(SplitterInNode).Quality;
2224 33467212 : MixerOutletMassFlowMaxAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail;
2225 33467212 : MixerOutletMassFlowMinAvail = state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail;
2226 33467212 : MixerOutletPress = state.dataLoopNodes->Node(SplitterInNode).Press;
2227 33467212 : break;
2228 : }
2229 : }
2230 :
2231 72250166 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate = MixerOutletMassFlow;
2232 72250166 : state.dataLoopNodes->Node(MixerOutletNode).Temp = MixerOutletTemp;
2233 72250166 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
2234 : // Don't update pressure, let pressure system handle this...
2235 : } else {
2236 : // Go ahead and update!
2237 72208046 : state.dataLoopNodes->Node(MixerOutletNode).Press = MixerOutletPress;
2238 : }
2239 72250166 : state.dataLoopNodes->Node(MixerOutletNode).Quality = MixerOutletQuality;
2240 :
2241 : // set max/min avails on mixer outlet to be consistent with the following rules
2242 : // 1. limited by the max/min avails on splitter inlet
2243 : // 2. limited by the sum of max/min avails for each branch's mixer inlet node
2244 :
2245 72250166 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMaxAvail =
2246 72250166 : min(MixerOutletMassFlowMaxAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMaxAvail);
2247 72250166 : state.dataLoopNodes->Node(MixerOutletNode).MassFlowRateMinAvail =
2248 72250166 : max(MixerOutletMassFlowMinAvail, state.dataLoopNodes->Node(SplitterInNode).MassFlowRateMinAvail);
2249 72250166 : }
2250 :
2251 72250166 : void HalfLoopData::UpdatePlantSplitter(EnergyPlusData &state)
2252 : {
2253 :
2254 : // SUBROUTINE INFORMATION:
2255 : // AUTHOR Brandon Anderson, Dan Fisher
2256 : // DATE WRITTEN October 1999
2257 : // MODIFIED na
2258 : // RE-ENGINEERED na
2259 :
2260 : // PURPOSE OF THIS SUBROUTINE:
2261 : // Set the outlet conditions of the splitter
2262 :
2263 : // Update Temperatures across splitter
2264 72250166 : if (this->Splitter.Exists) {
2265 :
2266 : // Set branch number at splitter inlet
2267 72250166 : int const SplitterInletNode = this->Splitter.NodeNumIn;
2268 :
2269 : // Loop over outlet nodes
2270 388712420 : for (int CurNode = 1; CurNode <= this->Splitter.TotalOutletNodes; ++CurNode) {
2271 316462254 : int const SplitterOutletNode = this->Splitter.NodeNumOut(CurNode);
2272 :
2273 : // Inlet Temp equals exit Temp to all outlet branches
2274 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).Temp = state.dataLoopNodes->Node(SplitterInletNode).Temp;
2275 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).TempMin = state.dataLoopNodes->Node(SplitterInletNode).TempMin;
2276 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).TempMax = state.dataLoopNodes->Node(SplitterInletNode).TempMax;
2277 316462254 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).HasPressureComponents) {
2278 : // Don't update pressure, let pressure system handle this...
2279 : } else {
2280 : // Go ahead and update!
2281 316408434 : state.dataLoopNodes->Node(SplitterOutletNode).Press = state.dataLoopNodes->Node(SplitterInletNode).Press;
2282 : }
2283 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).Quality = state.dataLoopNodes->Node(SplitterInletNode).Quality;
2284 :
2285 : // These two blocks and the following one which I added need to be cleaned up
2286 : // I think we will always pass maxavail down the splitter, min avail is the issue.
2287 : // Changed to include hardware max in next line
2288 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMaxAvail = min(
2289 316462254 : state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMax);
2290 316462254 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail = 0.0;
2291 :
2292 : // Not sure about passing min avail if it is nonzero. I am testing a pump with nonzero
2293 : // min flow rate, and it is causing problems because this routine passes zero down. Perhaps if
2294 : // it is a single parallel branch, we are safe to assume we need to just pass it down.
2295 : // But need to test for multiple branches (or at least think about it), to see what we need to do...
2296 316462254 : if (this->Splitter.TotalOutletNodes == 1) {
2297 1680448 : state.dataLoopNodes->Node(SplitterOutletNode).MassFlowRateMinAvail =
2298 1680448 : state.dataLoopNodes->Node(SplitterInletNode).MassFlowRateMinAvail;
2299 : }
2300 : }
2301 : }
2302 72250166 : }
2303 :
2304 : } // namespace DataPlant
2305 : } // namespace EnergyPlus
|