Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Array1D.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/BranchInputManager.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
60 : #include <EnergyPlus/DataLoopNode.hh>
61 : #include <EnergyPlus/DataSizing.hh>
62 : #include <EnergyPlus/FluidProperties.hh>
63 : #include <EnergyPlus/Plant/DataPlant.hh>
64 : #include <EnergyPlus/PlantUtilities.hh>
65 : #include <EnergyPlus/Pumps.hh>
66 : #include <EnergyPlus/UtilityRoutines.hh>
67 :
68 : namespace EnergyPlus::PlantUtilities {
69 :
70 : // Module containing the routines dealing with the <module_name>
71 :
72 : // MODULE INFORMATION:
73 : // AUTHOR <author>
74 : // DATE WRITTEN <date_written>
75 : // MODIFIED na
76 : // RE-ENGINEERED na
77 :
78 941 : void InitComponentNodes(EnergyPlusData &state, Real64 const MinCompMdot, Real64 const MaxCompMdot, int const InletNode, int const OutletNode)
79 : {
80 :
81 : // SUBROUTINE INFORMATION:
82 : // AUTHOR Brent Griffith
83 : // DATE WRITTEN Sept 2010
84 : // MODIFIED na
85 : // RE-ENGINEERED na
86 :
87 : // PURPOSE OF THIS SUBROUTINE:
88 : // Central routine for initializing plant nodes connected to components
89 : // typically used for BeginEnvrnFlag
90 :
91 : // METHODOLOGY EMPLOYED:
92 : // set MassFlowRate variables on inlet node
93 : // reset inlet node if more restrictive
94 :
95 : // Using/Aliasing
96 :
97 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
98 : Real64 tmpMinCompMdot; // local value
99 : Real64 tmpMaxCompMdot; // local value
100 :
101 941 : tmpMinCompMdot = MinCompMdot;
102 941 : tmpMaxCompMdot = MaxCompMdot;
103 : // trap bad values that can happen before all the setup is done
104 941 : if (tmpMinCompMdot < 0.0) tmpMinCompMdot = 0.0;
105 941 : if (tmpMaxCompMdot < 0.0) tmpMaxCompMdot = 0.0;
106 :
107 : // reset outlet node
108 941 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
109 :
110 941 : state.dataLoopNodes->Node(InletNode).MassFlowRateMin = tmpMinCompMdot;
111 941 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = tmpMinCompMdot;
112 941 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = tmpMaxCompMdot;
113 941 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = tmpMaxCompMdot;
114 : // reset inlet node, but only change from inlet setting if set and more restrictive
115 941 : state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
116 941 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = 0.0;
117 941 : }
118 :
119 725297 : void SetComponentFlowRate(EnergyPlusData &state,
120 : Real64 &CompFlow, // [kg/s]
121 : int const InletNode, // component's inlet node index in node structure
122 : int const OutletNode, // component's outlet node index in node structure
123 : PlantLocation const &plantLoc // component location for PlantLoop
124 : )
125 : {
126 :
127 : // SUBROUTINE INFORMATION:
128 : // AUTHOR Dan Fisher
129 : // DATE WRITTEN August 2009
130 : // MODIFIED na
131 : // RE-ENGINEERED na
132 :
133 : // PURPOSE OF THIS SUBROUTINE:
134 : // General purpose worker routine to set flows for a component model
135 :
136 725297 : if (plantLoc.loopNum == 0) { // protect from hard crash below
137 8 : if (InletNode > 0) {
138 16 : ShowSevereError(state,
139 16 : format("SetComponentFlowRate: trapped plant loop index = 0, check component with inlet node named={}",
140 8 : state.dataLoopNodes->NodeID(InletNode)));
141 : } else {
142 0 : ShowSevereError(state, "SetComponentFlowRate: trapped plant loop node id = 0");
143 : }
144 8 : return;
145 : // this crashes during ManageSizing, maybe it's just an init thing...
146 : // ShowFatalError(state, "Preceding loop index error causes program termination");
147 : }
148 :
149 725289 : Real64 const MdotOldRequest = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
150 :
151 725289 : if (plantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::Demand) {
152 : // store flow request on inlet node
153 366474 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
154 366474 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
155 366474 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
156 366474 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
157 366474 : min(state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMax);
158 : // virtual 2-way valve (was tried but it clamps down demand side component's flow options so they can't find proper solutions)
159 : } else {
160 : // lodge the original request for all types
161 358815 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
162 : }
163 :
164 : // Update Min/Max Avail
165 :
166 725289 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
167 725289 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
168 725289 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax >= 0.0) {
169 725208 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
170 725208 : min(state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMax);
171 : } else {
172 81 : if (!state.dataGlobal->SysSizingCalc && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
173 : // throw error for developers, need to change a component model to set hardware limits on inlet
174 17 : if (!state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued) {
175 28 : ShowSevereError(state,
176 28 : format("SetComponentFlowRate: check component model implementation for component with inlet node named={}",
177 14 : state.dataLoopNodes->NodeID(InletNode)));
178 14 : ShowContinueError(state, format("Inlet node MassFlowRatMax = {:.8R}", state.dataLoopNodes->Node(InletNode).MassFlowRateMax));
179 14 : state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued = true;
180 : }
181 : }
182 : }
183 :
184 : // Set loop flow rate
185 725289 : if (plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked) {
186 495306 : if (plantLoc.loop->MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
187 79 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
188 79 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
189 : } else { // bound the flow by Min/Max available and hardware limits
190 495227 : if (plantLoc.comp->FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
191 : // determine highest flow request for all the components on the branch
192 0 : Real64 SeriesBranchHighFlowRequest = 0.0;
193 0 : Real64 SeriesBranchHardwareMaxLim = state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
194 0 : Real64 SeriesBranchHardwareMinLim = 0.0;
195 0 : Real64 SeriesBranchMaxAvail = state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail;
196 0 : Real64 SeriesBranchMinAvail = 0.0;
197 :
198 : // inserting EMS On/Off Supervisory control here to series branch constraint and assuming EMS should shut off flow completely
199 : // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
200 0 : bool EMSLoadOverride = false;
201 :
202 0 : for (int CompNum = 1; CompNum <= plantLoc.branch->TotalComponents; ++CompNum) {
203 0 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
204 0 : int const CompInletNodeNum = thisComp.NodeNumIn;
205 0 : auto const &thisInletNode = state.dataLoopNodes->Node(CompInletNodeNum);
206 0 : SeriesBranchHighFlowRequest = max(thisInletNode.MassFlowRateRequest, SeriesBranchHighFlowRequest);
207 0 : SeriesBranchHardwareMaxLim = min(thisInletNode.MassFlowRateMax, SeriesBranchHardwareMaxLim);
208 0 : SeriesBranchHardwareMinLim = max(thisInletNode.MassFlowRateMin, SeriesBranchHardwareMinLim);
209 0 : SeriesBranchMaxAvail = min(thisInletNode.MassFlowRateMaxAvail, SeriesBranchMaxAvail);
210 0 : SeriesBranchMinAvail = max(thisInletNode.MassFlowRateMinAvail, SeriesBranchMinAvail);
211 : // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
212 0 : if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
213 : }
214 :
215 0 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
216 0 : SeriesBranchHardwareMaxLim = 0.0;
217 : }
218 :
219 : // take higher of branch max flow request and this new flow request
220 0 : CompFlow = max(CompFlow, SeriesBranchHighFlowRequest);
221 :
222 : // apply constraints on component flow
223 0 : CompFlow = max(CompFlow, SeriesBranchHardwareMinLim);
224 0 : CompFlow = max(CompFlow, SeriesBranchMinAvail);
225 0 : CompFlow = min(CompFlow, SeriesBranchHardwareMaxLim);
226 0 : CompFlow = min(CompFlow, SeriesBranchMaxAvail);
227 :
228 0 : if (CompFlow < DataBranchAirLoopPlant::MassFlowTolerance) CompFlow = 0.0;
229 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
230 0 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
231 0 : for (int CompNum = 1; CompNum <= plantLoc.branch->TotalComponents; ++CompNum) {
232 0 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
233 0 : int const CompInletNodeNum = thisComp.NodeNumIn;
234 0 : int const CompOutletNodeNum = thisComp.NodeNumOut;
235 0 : state.dataLoopNodes->Node(CompInletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
236 0 : state.dataLoopNodes->Node(CompOutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
237 : }
238 :
239 : } else { // not series active
240 495227 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = max(state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail, CompFlow);
241 495227 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
242 495227 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMin, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
243 495227 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
244 495227 : min(state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
245 495227 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
246 495227 : min(state.dataLoopNodes->Node(InletNode).MassFlowRateMax, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
247 :
248 : // inserting EMS On/Off Supervisory control here to override min constraint assuming EMS should shut off flow completely
249 : // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
250 495227 : bool EMSLoadOverride = false;
251 :
252 990525 : for (int CompNum = 1; CompNum <= plantLoc.branch->TotalComponents; ++CompNum) {
253 : // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
254 495298 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
255 495298 : if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
256 : }
257 :
258 495227 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
259 2 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
260 : }
261 :
262 495227 : if (state.dataLoopNodes->Node(OutletNode).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance)
263 223778 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
264 495227 : CompFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
265 495227 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
266 : }
267 : }
268 229983 : } else if (plantLoc.side->FlowLock == DataPlant::FlowLock::Locked) {
269 229983 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
270 229983 : CompFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
271 : } else {
272 : ShowFatalError(state, "SetComponentFlowRate: Flow lock out of range"); // DEBUG error...should never get here LCOV_EXCL_LINE
273 : }
274 :
275 725289 : if (plantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::Demand) {
276 366474 : if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
277 182348 : if (std::abs(MdotOldRequest - state.dataLoopNodes->Node(InletNode).MassFlowRateRequest) >
278 : DataBranchAirLoopPlant::MassFlowTolerance) { // demand comp changed its flow request
279 100537 : plantLoc.side->SimLoopSideNeeded = true;
280 : }
281 : }
282 : }
283 : }
284 :
285 228545 : void SetActuatedBranchFlowRate(EnergyPlusData &state,
286 : Real64 &CompFlow,
287 : int const ActuatedNode,
288 : PlantLocation const &plantLoc,
289 : bool const ResetMode // flag to indicate if this is a real flow set, or a reset flow setting.
290 : )
291 : {
292 :
293 : // SUBROUTINE INFORMATION:
294 : // AUTHOR B. Griffith
295 : // DATE WRITTEN Feb 2010
296 : // MODIFIED na
297 : // RE-ENGINEERED na
298 :
299 : // PURPOSE OF THIS SUBROUTINE:
300 : // general purpse worker routine to set plant node variables for node
301 : // and all nodes on the branch. Used by HVAC water coil controller, that do not
302 : // distinguish single component and have no inlet-outlet pair
303 : // only a actuated noded of no clear position. set flow on entire branch
304 :
305 : // METHODOLOGY EMPLOYED:
306 : // Set flow on node and branch while honoring constraints on actuated node
307 :
308 228545 : auto &a_node = state.dataLoopNodes->Node(ActuatedNode);
309 228545 : if (plantLoc.loopNum == 0 || plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
310 : // early in simulation before plant loops are setup and found
311 1 : a_node.MassFlowRate = CompFlow;
312 1 : return;
313 : }
314 :
315 228544 : auto &loop_side = state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum);
316 :
317 : // store original flow
318 228544 : Real64 const MdotOldRequest = a_node.MassFlowRateRequest;
319 228544 : a_node.MassFlowRateRequest = CompFlow;
320 228544 : if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid && (!ResetMode)) {
321 195395 : if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
322 175431 : if ((std::abs(MdotOldRequest - a_node.MassFlowRateRequest) > DataBranchAirLoopPlant::MassFlowTolerance) &&
323 70037 : (loop_side.FlowLock == DataPlant::FlowLock::Unlocked)) {
324 70037 : loop_side.SimLoopSideNeeded = true;
325 : }
326 : }
327 : }
328 : // Set loop flow rate
329 :
330 228544 : if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid) {
331 228544 : auto const &branch = loop_side.Branch(plantLoc.branchNum);
332 228544 : if (loop_side.FlowLock == DataPlant::FlowLock::Unlocked) {
333 228538 : if (state.dataPlnt->PlantLoop(plantLoc.loopNum).MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
334 85 : a_node.MassFlowRate = CompFlow;
335 : } else { // bound the flow by Min/Max available across entire branch
336 :
337 228453 : a_node.MassFlowRate = max(a_node.MassFlowRateMinAvail, CompFlow);
338 228453 : a_node.MassFlowRate = max(a_node.MassFlowRateMin, a_node.MassFlowRate);
339 : // add MassFlowRateMin hardware constraints
340 :
341 : // inserting EMS On/Off Supervisory control here to override min constraint assuming EMS should shut off flow completely
342 : // action here means EMS will not impact the FlowLock == FlowLocked condition (which should still show EMS intent)
343 228453 : bool EMSLoadOverride = false;
344 : // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
345 456912 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
346 228459 : auto const &comp = branch.Comp(CompNum);
347 228459 : if (comp.EMSLoadOverrideOn && comp.EMSLoadOverrideValue == 0.0) EMSLoadOverride = true;
348 : }
349 228453 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
350 2 : a_node.MassFlowRate = 0.0;
351 2 : a_node.MassFlowRateRequest = 0.0;
352 : }
353 :
354 228453 : a_node.MassFlowRate = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRate);
355 228453 : a_node.MassFlowRate = min(a_node.MassFlowRateMax, a_node.MassFlowRate);
356 228453 : if (a_node.MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) a_node.MassFlowRate = 0.0;
357 456912 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
358 228459 : auto const &comp = branch.Comp(CompNum);
359 228459 : if (ActuatedNode == comp.NodeNumIn) {
360 : // ! found controller set to inlet of a component. now set that component's outlet
361 228453 : int const NodeNum = comp.NodeNumOut;
362 228453 : state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail = max(a_node.MassFlowRateMinAvail, a_node.MassFlowRateMin);
363 228453 : state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRateMax);
364 228453 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node.MassFlowRate;
365 : }
366 : }
367 : }
368 :
369 6 : } else if (loop_side.FlowLock == DataPlant::FlowLock::Locked) {
370 :
371 6 : CompFlow = a_node.MassFlowRate;
372 : // do not change requested flow rate either
373 6 : a_node.MassFlowRateRequest = MdotOldRequest;
374 6 : if ((CompFlow - a_node.MassFlowRateMaxAvail > DataBranchAirLoopPlant::MassFlowTolerance) ||
375 6 : (a_node.MassFlowRateMinAvail - CompFlow > DataBranchAirLoopPlant::MassFlowTolerance)) {
376 0 : ShowSevereError(state, "SetActuatedBranchFlowRate: Flow rate is out of range"); // DEBUG error...should never get here
377 0 : ShowContinueErrorTimeStamp(state, "");
378 0 : ShowContinueError(state, format("Component flow rate [kg/s] = {:.8R}", CompFlow));
379 0 : ShowContinueError(state, format("Node maximum flow rate available [kg/s] = {:.8R}", a_node.MassFlowRateMaxAvail));
380 0 : ShowContinueError(state, format("Node minimum flow rate available [kg/s] = {:.8R}", a_node.MassFlowRateMinAvail));
381 : }
382 : } else {
383 0 : ShowFatalError(state,
384 0 : format("SetActuatedBranchFlowRate: Flowlock out of range, value={}",
385 : loop_side.FlowLock)); // DEBUG error...should never get here LCOV_EXCL_LINE
386 : }
387 :
388 228544 : Real64 const a_node_MasFlowRate(a_node.MassFlowRate);
389 228544 : Real64 const a_node_MasFlowRateRequest(a_node.MassFlowRateRequest);
390 457094 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
391 228550 : auto const &comp = branch.Comp(CompNum);
392 228550 : int NodeNum = comp.NodeNumIn;
393 228550 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
394 228550 : state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
395 228550 : NodeNum = comp.NodeNumOut;
396 228550 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
397 228550 : state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
398 : }
399 : }
400 : }
401 :
402 28540 : Real64 RegulateCondenserCompFlowReqOp(EnergyPlusData &state, PlantLocation const &plantLoc, Real64 const TentativeFlowRequest)
403 : {
404 :
405 : // FUNCTION INFORMATION:
406 : // AUTHOR Edwin Lee
407 : // DATE WRITTEN April 2012
408 : // MODIFIED na
409 : // RE-ENGINEERED na
410 :
411 : // PURPOSE OF THIS FUNCTION:
412 : // This function will do some intelligent flow request logic for condenser equipment.
413 : // Some condenser equipment (ground heat exchangers, etc.) may not have a meaningful load value
414 : // since this is an environment heat transfer component.
415 : // The runflag is set, but may not be properly set, and the component may still request flow even
416 : // when it doesn't need to.
417 : // This function will do a little more advanced logic than just checking runflag to determine whether
418 : // to request any flow
419 :
420 : // METHODOLOGY EMPLOYED:
421 : // Query run flag and MyLoad
422 : // If run flag is OFF, then the component should actually be OFF, and tentative flow request will be zeroed
423 : // If the run flag is ON, then check the control type to determine if MyLoad is a meaningful value
424 : // If it is meaningful then determine whether to do flow request based on MyLoad
425 : // If not then we will have no choice but to leave the flow request alone (uncontrolled operation?)
426 :
427 : // Using/Aliasing
428 :
429 : // Return value
430 : Real64 FlowVal;
431 :
432 : // FUNCTION PARAMETER DEFINITIONS:
433 28540 : Real64 constexpr ZeroLoad(0.0001);
434 :
435 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
436 : Real64 CompCurLoad;
437 : bool CompRunFlag;
438 :
439 28540 : CompCurLoad = DataPlant::CompData::getPlantComponent(state, plantLoc).MyLoad;
440 28540 : CompRunFlag = DataPlant::CompData::getPlantComponent(state, plantLoc).ON;
441 28540 : DataPlant::OpScheme CompOpScheme = DataPlant::CompData::getPlantComponent(state, plantLoc).CurOpSchemeType;
442 :
443 28540 : if (CompRunFlag) {
444 :
445 22313 : switch (CompOpScheme) {
446 :
447 8501 : case DataPlant::OpScheme::HeatingRB:
448 : case DataPlant::OpScheme::CoolingRB:
449 : case DataPlant::OpScheme::CompSetPtBased: { // These provide meaningful MyLoad values
450 8501 : if (std::abs(CompCurLoad) > ZeroLoad) {
451 8498 : FlowVal = TentativeFlowRequest;
452 : } else { // no load
453 3 : FlowVal = 0.0;
454 : }
455 8501 : break;
456 : }
457 13812 : default: { // Types that don't provide meaningful MyLoad values
458 13812 : FlowVal = TentativeFlowRequest;
459 13812 : break;
460 : }
461 : }
462 :
463 : } else { // runflag OFF
464 :
465 6227 : FlowVal = 0.0;
466 : }
467 :
468 28540 : return FlowVal;
469 : }
470 :
471 12437 : bool AnyPlantSplitterMixerLacksContinuity(EnergyPlusData &state)
472 : {
473 :
474 : // FUNCTION INFORMATION:
475 : // AUTHOR B. Griffith
476 : // DATE WRITTEN April 2012
477 : // MODIFIED na
478 : // RE-ENGINEERED na
479 :
480 : // PURPOSE OF THIS FUNCTION:
481 : // Similar to CheckPlantMixerSplitterConsistency, but used to decide if plant needs to iterate again
482 33913 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
483 64432 : for (DataPlant::LoopSideLocation LoopSide : DataPlant::LoopSideKeys) {
484 42956 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.Exists) {
485 42952 : int const SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.NodeNumIn;
486 : // loop across branch outlet nodes and check mass continuity
487 42952 : int const NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.TotalOutletNodes;
488 42952 : Real64 SumOutletFlow = 0.0;
489 152278 : for (int OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
490 109326 : int const BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.BranchNumOut(OutletNum);
491 109326 : int const LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Branch(BranchNum).NodeNumOut;
492 109326 : SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
493 : }
494 42952 : Real64 const AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
495 42952 : if (AbsDifference > DataPlant::CriteriaDelta_MassFlowRate) {
496 2 : return true;
497 : }
498 : }
499 : }
500 : }
501 12435 : return false;
502 : }
503 :
504 42948 : void CheckPlantMixerSplitterConsistency(EnergyPlusData &state,
505 : int const LoopNum,
506 : const DataPlant::LoopSideLocation LoopSideNum,
507 : bool const FirstHVACIteration)
508 : {
509 :
510 : // SUBROUTINE INFORMATION:
511 : // AUTHOR Brent Griffith
512 : // DATE WRITTEN Oct 2007
513 : // MODIFIED na
514 : // RE-ENGINEERED na
515 :
516 : // PURPOSE OF THIS SUBROUTINE:
517 : // Check for plant flow resolver errors
518 :
519 : // METHODOLOGY EMPLOYED:
520 : // compare flow rate of splitter inlet to flow rate of mixer outlet
521 :
522 : // Using/Aliasing
523 : using DataPlant::CriteriaDelta_MassFlowRate;
524 :
525 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
526 : Real64 AbsDifference;
527 : Real64 SumOutletFlow;
528 :
529 42948 : if (!state.dataPlnt->PlantLoop(LoopNum).LoopHasConnectionComp) {
530 85896 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag &&
531 85896 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Exists && !FirstHVACIteration) {
532 : // Find mixer outlet node number
533 7666 : int MixerOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.NodeNumOut;
534 : // Find splitter inlet node number
535 7666 : int SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.NodeNumIn;
536 :
537 7666 : AbsDifference =
538 7666 : std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate);
539 7666 : if (AbsDifference > DataBranchAirLoopPlant::MassFlowTolerance) {
540 0 : if (state.dataPlnt->PlantLoop(LoopNum).MFErrIndex1 == 0) {
541 0 : ShowSevereMessage(state, "Plant flows do not resolve -- splitter inlet flow does not match mixer outlet flow ");
542 0 : ShowContinueErrorTimeStamp(state, "");
543 0 : ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
544 0 : ShowContinueError(state,
545 0 : format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
546 0 : ShowContinueError(
547 0 : state, format("Mixer outlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate));
548 0 : ShowContinueError(
549 0 : state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
550 0 : ShowContinueError(
551 0 : state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
552 0 : ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
553 : }
554 0 : ShowRecurringSevereErrorAtEnd(state,
555 0 : "Plant Flows (Loop=" + state.dataPlnt->PlantLoop(LoopNum).Name +
556 : ") splitter inlet flow not match mixer outlet flow",
557 0 : state.dataPlnt->PlantLoop(LoopNum).MFErrIndex1,
558 : AbsDifference,
559 : AbsDifference,
560 : _,
561 : "kg/s",
562 : "kg/s");
563 0 : if (AbsDifference > DataBranchAirLoopPlant::MassFlowTolerance * 10.0) {
564 0 : ShowSevereError(state, "Plant flows do not resolve -- splitter inlet flow does not match mixer outlet flow ");
565 0 : ShowContinueErrorTimeStamp(state, "");
566 0 : ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
567 0 : ShowContinueError(state,
568 0 : format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
569 0 : ShowContinueError(
570 0 : state, format("Mixer outlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate));
571 0 : ShowContinueError(
572 0 : state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
573 0 : ShowContinueError(
574 0 : state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
575 0 : ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
576 0 : ShowFatalError(state, "CheckPlantMixerSplitterConsistency: Simulation terminated because of problems in plant flow resolver");
577 : }
578 : }
579 :
580 : // now check inside s/m to see if there are problems
581 :
582 : // loop across branch outlet nodes and check mass continuity
583 7666 : int NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes;
584 7666 : SumOutletFlow = 0.0;
585 : // SumInletFlow = 0.0;
586 25045 : for (int OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
587 17379 : int BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.BranchNumOut(OutletNum);
588 17379 : int LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumOut;
589 17379 : SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
590 : // FirstNodeOnBranch= PlantLoop(LoopNum)%LoopSide(LoopSideNum)%Branch(BranchNum)%NodeNumIn
591 : // SumInletFlow = SumInletFlow + Node(FirstNodeOnBranch)%MassFlowRate
592 : }
593 7666 : AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
594 7666 : if (AbsDifference > CriteriaDelta_MassFlowRate) {
595 0 : if (state.dataPlnt->PlantLoop(LoopNum).MFErrIndex2 == 0) {
596 0 : ShowSevereMessage(state, "Plant flows do not resolve -- splitter inlet flow does not match branch outlet flows");
597 0 : ShowContinueErrorTimeStamp(state, "");
598 0 : ShowContinueError(state, format("PlantLoop name= {}", state.dataPlnt->PlantLoop(LoopNum).Name));
599 0 : ShowContinueError(state,
600 0 : format("Plant Connector:Mixer name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Name));
601 0 : ShowContinueError(state, format("Sum of Branch outlet mass flow rates= {:.6R} {{kg/s}}", SumOutletFlow));
602 0 : ShowContinueError(
603 0 : state, format("Plant Connector:Splitter name= {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.Name));
604 0 : ShowContinueError(
605 0 : state, format("Splitter inlet mass flow rate= {:.6R} {{kg/s}}", state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate));
606 0 : ShowContinueError(state, format("Difference in two mass flow rates= {:.6R} {{kg/s}}", AbsDifference));
607 : }
608 0 : ShowRecurringSevereErrorAtEnd(state,
609 0 : "Plant Flows (Loop=" + state.dataPlnt->PlantLoop(LoopNum).Name +
610 : ") splitter inlet flow does not match branch outlet flows",
611 0 : state.dataPlnt->PlantLoop(LoopNum).MFErrIndex2,
612 : AbsDifference,
613 : AbsDifference,
614 : _,
615 : "kg/s",
616 : "kg/s");
617 : }
618 : }
619 : }
620 42948 : }
621 :
622 42948 : void CheckForRunawayPlantTemps(EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation LoopSideNum)
623 : {
624 :
625 : // SUBROUTINE INFORMATION:
626 : // AUTHOR Brent Griffith
627 : // DATE WRITTEN Sept 2010
628 : // MODIFIED na
629 : // RE-ENGINEERED na
630 :
631 : // PURPOSE OF THIS SUBROUTINE:
632 : // Check for plant control errors revealed as run away fluid temps
633 : // halt program so it won't siliently run in out of control state
634 :
635 : // METHODOLOGY EMPLOYED:
636 : // compare plant temps to plant min and max and halt if things run away
637 : // sensitivity can be adjusted with parameters, picked somewhat arbitrary
638 :
639 : // REFERENCES:
640 : // na
641 :
642 : // Using/Aliasing
643 :
644 : // Locals
645 : // SUBROUTINE ARGUMENT DEFINITIONS:
646 :
647 : // SUBROUTINE PARAMETER DEFINITIONS:
648 42948 : Real64 constexpr OverShootOffset(5.0);
649 42948 : Real64 constexpr UnderShootOffset(5.0);
650 42948 : Real64 constexpr FatalOverShootOffset(200.0);
651 42948 : Real64 constexpr FatalUnderShootOffset(100.0);
652 : // INTERFACE BLOCK SPECIFICATIONS:
653 : // na
654 :
655 : // DERIVED TYPE DEFINITIONS:
656 : // na
657 :
658 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
659 42948 : std::string hotcold;
660 : bool makefatalerror;
661 : Real64 LoopCapacity;
662 : Real64 LoopDemandSideCapacity;
663 : Real64 LoopSupplySideCapacity;
664 : Real64 DispatchedCapacity;
665 : Real64 LoopDemandSideDispatchedCapacity;
666 : Real64 LoopSupplySideDispatchedCapacity;
667 :
668 42948 : makefatalerror = false;
669 42948 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
670 42948 : (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + OverShootOffset)) {
671 :
672 : // first stage, throw recurring warning that plant loop is getting out of control
673 4508 : ShowRecurringWarningErrorAtEnd(state,
674 1288 : "Plant loop exceeding upper temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name + "\"",
675 644 : state.dataPlnt->PlantLoop(LoopNum).MaxTempErrIndex,
676 644 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
677 :
678 644 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
679 644 : (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + FatalOverShootOffset)) {
680 0 : hotcold = "hot";
681 0 : makefatalerror = true;
682 : }
683 : }
684 :
685 42948 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
686 42948 : (state.dataPlnt->PlantLoop(LoopNum).MinTemp - UnderShootOffset)) {
687 :
688 : // first stage, throw recurring warning that plant loop is getting out of control
689 0 : ShowRecurringWarningErrorAtEnd(state,
690 0 : "Plant loop falling below lower temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name +
691 : "\"",
692 0 : state.dataPlnt->PlantLoop(LoopNum).MinTempErrIndex,
693 : _,
694 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
695 :
696 0 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
697 0 : (state.dataPlnt->PlantLoop(LoopNum).MinTemp - FatalUnderShootOffset)) {
698 0 : hotcold = "cold";
699 0 : makefatalerror = true;
700 : }
701 : }
702 :
703 42948 : if (makefatalerror) {
704 0 : ShowSevereError(state, format("Plant temperatures are getting far too {}, check controls and relative loads and capacities", hotcold));
705 0 : ShowContinueErrorTimeStamp(state, "");
706 0 : ShowContinueError(state,
707 0 : format("PlantLoop Name ({} Side) = {}",
708 0 : DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
709 0 : state.dataPlnt->PlantLoop(LoopNum).Name));
710 0 : ShowContinueError(state,
711 0 : format("PlantLoop Setpoint Temperature={:.1R} {{C}}",
712 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).TempSetPointNodeNum).TempSetPoint));
713 0 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt) {
714 0 : ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Supply) has a Setpoint.");
715 : } else {
716 0 : ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Supply) does not have a Setpoint.");
717 : }
718 0 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
719 0 : ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Demand) has a Setpoint.");
720 : } else {
721 0 : ShowContinueError(state, "PlantLoop Inlet Node (LoopSideLocation::Demand) does not have a Setpoint.");
722 : }
723 0 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).OutletNodeSetPt) {
724 0 : ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Supply) has a Setpoint.");
725 : } else {
726 0 : ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Supply) does not have a Setpoint.");
727 : }
728 0 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).OutletNodeSetPt) {
729 0 : ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Demand) has a Setpoint.");
730 : } else {
731 0 : ShowContinueError(state, "PlantLoop Outlet Node (LoopSideLocation::Demand) does not have a Setpoint.");
732 : }
733 0 : ShowContinueError(state,
734 0 : format("PlantLoop Outlet Node ({}Side) \"{}\" has temperature={:.1R} {{C}}",
735 0 : DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
736 0 : state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut),
737 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp));
738 0 : ShowContinueError(state,
739 0 : format("PlantLoop Inlet Node ({}Side) \"{}\" has temperature={:.1R} {{C}}",
740 0 : DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
741 0 : state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumIn),
742 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumIn).Temp));
743 0 : ShowContinueError(state, format("PlantLoop Minimum Temperature={:.1R} {{C}}", state.dataPlnt->PlantLoop(LoopNum).MinTemp));
744 0 : ShowContinueError(state, format("PlantLoop Maximum Temperature={:.1R} {{C}}", state.dataPlnt->PlantLoop(LoopNum).MaxTemp));
745 0 : ShowContinueError(state,
746 0 : format("PlantLoop Flow Request (LoopSideLocation::Supply)={:.1R} {{kg/s}}",
747 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).FlowRequest));
748 0 : ShowContinueError(state,
749 0 : format("PlantLoop Flow Request (LoopSideLocation::Demand)={:.1R} {{kg/s}}",
750 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).FlowRequest));
751 0 : ShowContinueError(state,
752 0 : format("PlantLoop Node ({}Side) \"{}\" has mass flow rate ={:.1R} {{kg/s}}",
753 0 : DataPlant::DemandSupplyNames[static_cast<int>(LoopSideNum)],
754 0 : state.dataLoopNodes->NodeID(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut),
755 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).MassFlowRate));
756 0 : ShowContinueError(state,
757 0 : format("PlantLoop PumpHeat (LoopSideLocation::Supply)={:.1R} {{W}}",
758 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).TotalPumpHeat));
759 0 : ShowContinueError(state,
760 0 : format("PlantLoop PumpHeat (LoopSideLocation::Demand)={:.1R} {{W}}",
761 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).TotalPumpHeat));
762 0 : ShowContinueError(state, format("PlantLoop Cooling Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).CoolingDemand));
763 0 : ShowContinueError(state, format("PlantLoop Heating Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).HeatingDemand));
764 0 : ShowContinueError(state, format("PlantLoop Demand not Dispatched={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).DemandNotDispatched));
765 0 : ShowContinueError(state, format("PlantLoop Unmet Demand={:.1R} {{W}}", state.dataPlnt->PlantLoop(LoopNum).UnmetDemand));
766 :
767 0 : LoopCapacity = 0.0;
768 0 : DispatchedCapacity = 0.0;
769 0 : for (DataPlant::LoopSideLocation LSN : DataPlant::LoopSideKeys) {
770 0 : for (int BrN = 1; BrN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).TotalBranches; ++BrN) {
771 0 : for (int CpN = 1; CpN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).TotalComponents; ++CpN) {
772 0 : LoopCapacity += state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).MaxLoad;
773 0 : DispatchedCapacity += std::abs(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).MyLoad);
774 : }
775 : }
776 0 : if (LSN == DataPlant::LoopSideLocation::Demand) {
777 0 : LoopDemandSideCapacity = LoopCapacity;
778 0 : LoopDemandSideDispatchedCapacity = DispatchedCapacity;
779 : } else {
780 0 : LoopSupplySideCapacity = LoopCapacity - LoopDemandSideCapacity;
781 0 : LoopSupplySideDispatchedCapacity = DispatchedCapacity - LoopDemandSideDispatchedCapacity;
782 : }
783 : }
784 0 : ShowContinueError(state, format("PlantLoop Capacity={:.1R} {{W}}", LoopCapacity));
785 0 : ShowContinueError(state, format("PlantLoop Capacity (LoopSideLocation::Supply)={:.1R} {{W}}", LoopSupplySideCapacity));
786 0 : ShowContinueError(state, format("PlantLoop Capacity (LoopSideLocation::Demand)={:.1R} {{W}}", LoopDemandSideCapacity));
787 0 : ShowContinueError(state, format("PlantLoop Operation Scheme={}", state.dataPlnt->PlantLoop(LoopNum).OperationScheme));
788 0 : ShowContinueError(state, format("PlantLoop Operation Dispatched Load = {:.1R} {{W}}", DispatchedCapacity));
789 0 : ShowContinueError(state,
790 0 : format("PlantLoop Operation Dispatched Load (LoopSideLocation::Supply)= {:.1R} {{W}}", LoopSupplySideDispatchedCapacity));
791 0 : ShowContinueError(state,
792 0 : format("PlantLoop Operation Dispatched Load (LoopSideLocation::Demand)= {:.1R} {{W}}", LoopDemandSideDispatchedCapacity));
793 0 : ShowContinueError(state, "Branches on the Loop.");
794 0 : ShowBranchesOnLoop(state, LoopNum);
795 0 : ShowContinueError(state, "*************************");
796 0 : ShowContinueError(state, "Possible things to look for to correct this problem are:");
797 0 : ShowContinueError(state, " Capacity, Operation Scheme, Mass flow problems, Pump Heat building up over time.");
798 0 : ShowContinueError(state, " Try a shorter runperiod to stop before it fatals and look at");
799 0 : ShowContinueError(state, " lots of node time series data to see what is going wrong.");
800 0 : ShowContinueError(state, " If this is happening during Warmup, you can use Output:Diagnostics,ReportDuringWarmup;");
801 0 : ShowContinueError(state, " This is detected at the loop level, but the typical problems are in the components.");
802 0 : ShowFatalError(state, format("CheckForRunawayPlantTemps: Simulation terminated because of run away plant temperatures, too {}", hotcold));
803 : }
804 42948 : }
805 :
806 424800 : void SetAllFlowLocks(EnergyPlusData &state, DataPlant::FlowLock const Value)
807 : {
808 :
809 : // SUBROUTINE INFORMATION:
810 : // AUTHOR Edwin Lee
811 : // DATE WRITTEN November 2009
812 : // MODIFIED na
813 : // RE-ENGINEERED na
814 :
815 : // PURPOSE OF THIS SUBROUTINE:
816 : // This subroutine will set both LoopSide flowlocks on all plant loops to the input value (0 or 1)
817 : // Initially this routine is used as a quick replacement for the FlowLock=0 and FlowLock=1 statements
818 : // in order to provide the same behavior through phase I of the demand side rewrite
819 : // Eventually this routine may be employed again to quickly initialize all loops once phase III is complete
820 467789 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
821 128967 : for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
822 85978 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).FlowLock = Value;
823 : }
824 : }
825 424800 : }
826 :
827 424777 : void ResetAllPlantInterConnectFlags(EnergyPlusData &state)
828 : {
829 :
830 : // SUBROUTINE INFORMATION:
831 : // AUTHOR Edwin Lee
832 : // DATE WRITTEN September 2010
833 : // MODIFIED na
834 : // RE-ENGINEERED na
835 :
836 : // PURPOSE OF THIS SUBROUTINE:
837 : // This subroutine will reset all interconnected (air, zone, etc.) sim flags for both loopsides of all loops
838 :
839 467727 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
840 128850 : for (auto &e : state.dataPlnt->PlantLoop(LoopNum).LoopSide) {
841 85900 : e.SimAirLoopsNeeded = false;
842 85900 : e.SimZoneEquipNeeded = false;
843 85900 : e.SimNonZoneEquipNeeded = false;
844 85900 : e.SimElectLoadCentrNeeded = false;
845 : }
846 : }
847 424777 : }
848 :
849 563 : void PullCompInterconnectTrigger(EnergyPlusData &state,
850 : const PlantLocation &plantLoc, // Component Location
851 : int &UniqueCriteriaCheckIndex, // An integer given to this particular check
852 : const PlantLocation &ConnectedPlantLoc, // Interconnected Component's Location
853 : const DataPlant::CriteriaType CriteriaType, // The criteria check to use, see DataPlant: SimFlagCriteriaTypes
854 : const Real64 CriteriaValue // The value of the criteria check to evaluate
855 : )
856 : {
857 :
858 : // SUBROUTINE INFORMATION:
859 : // AUTHOR Edwin Lee
860 : // DATE WRITTEN September 2010
861 :
862 : // PURPOSE OF THIS SUBROUTINE:
863 : // Provides a generic means for components to trigger interconnected loop sides sim flags
864 :
865 : // METHODOLOGY EMPLOYED:
866 : // Determine convergence criteria based on *CriteriaType* variable. This routine only turns
867 : // the loop side sim flag ON, it doesn't turn it OFF.
868 : // The convergence value history was originally going to be put at the Branch()%Comp()%...
869 : // level, but this would be quite difficult if we had multiple convergence checks for the
870 : // same component, such as if a chiller was trying to turn on the condenser side and the
871 : // heat recovery side.
872 : // It was determined to use a local array, which is only reallocated during the first stages
873 : // of the simulation when components are first calling their sim flag requests. After that
874 : // the INOUT index variable will be used to avoid reallocation and string compares.
875 : // Error handling will be put in to ensure unique identifiers are used for debugging purposes.
876 : // A single component may have multiple check indeces, but a single index will only have one
877 : // associated component. Therefore whenever we come in with a non-zero index, we will just
878 : // verify that the stored loop/side/branch/comp matches
879 :
880 563 : if (UniqueCriteriaCheckIndex <= 0) { // If we don't yet have an index, we need to initialize
881 :
882 : // We need to start by allocating, or REallocating the array
883 15 : int const CurrentNumChecksStored(static_cast<int>(state.dataPlantUtilities->CriteriaChecks.size() + 1));
884 15 : state.dataPlantUtilities->CriteriaChecks.redimension(CurrentNumChecksStored);
885 :
886 : // Store the unique name and location
887 15 : state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopNum = plantLoc.loopNum;
888 15 : state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopSideNum = plantLoc.loopSideNum;
889 :
890 15 : if (plantLoc.loopNum == 0 || plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
891 0 : assert(false); // check that component has been set up correctly
892 : }
893 : // Since this was the first pass, it is safe to assume something has changed!
894 : // Therefore we'll set the sim flag to true
895 15 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
896 :
897 : // Make sure we return the proper value of index
898 15 : UniqueCriteriaCheckIndex = CurrentNumChecksStored;
899 :
900 : } else { // We already have an index
901 :
902 : // If we have an index, we need to do a brief error handling, then determine
903 : // sim flag status based on the criteria type
904 :
905 : // First store the current check in a single variable instead of array for readability
906 548 : CriteriaData CurCriteria = state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex);
907 :
908 : // Initialize, then check if we are out of range
909 548 : switch (CriteriaType) {
910 542 : case DataPlant::CriteriaType::MassFlowRate: {
911 542 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_MassFlowRate) {
912 55 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
913 : }
914 542 : } break;
915 3 : case DataPlant::CriteriaType::Temperature: {
916 3 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_Temperature) {
917 1 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
918 : }
919 3 : } break;
920 3 : case DataPlant::CriteriaType::HeatTransferRate: {
921 3 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_HeatTransferRate) {
922 1 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
923 : }
924 3 : } break;
925 0 : default:
926 0 : assert(false);
927 : break;
928 : }
929 :
930 : } // if we have an index or not
931 :
932 : // Store the value for the next pass
933 563 : state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex).ThisCriteriaCheckValue = CriteriaValue;
934 563 : }
935 :
936 14321 : void UpdateChillerComponentCondenserSide(EnergyPlusData &state,
937 : int const LoopNum, // component's loop index
938 : const DataPlant::LoopSideLocation LoopSide, // component's loop side number
939 : [[maybe_unused]] DataPlant::PlantEquipmentType Type, // Component's type index
940 : int const InletNodeNum, // Component's inlet node pointer
941 : int const OutletNodeNum, // Component's outlet node pointer
942 : Real64 const ModelCondenserHeatRate, // model's heat rejection rate at condenser (W)
943 : Real64 const ModelInletTemp, // model's inlet temperature (C)
944 : Real64 const ModelOutletTemp, // model's outlet temperature (C)
945 : Real64 const ModelMassFlowRate, // model's condenser water mass flow rate (kg/s)
946 : bool const FirstHVACIteration)
947 : {
948 :
949 : // SUBROUTINE INFORMATION:
950 : // AUTHOR Brent Griffith
951 : // DATE WRITTEN February 2010
952 : // MODIFIED na
953 : // RE-ENGINEERED na
954 :
955 : // PURPOSE OF THIS SUBROUTINE:
956 : // provides reusable update routine for water cooled chiller's condenser water
957 : // connection to plant loops
958 :
959 : // METHODOLOGY EMPLOYED:
960 : // check if anything changed or doesn't agree and set simulation flags.
961 : // update outlet conditions if needed or possible
962 :
963 : // SUBROUTINE PARAMETER DEFINITIONS:
964 : static constexpr std::string_view RoutineName("UpdateChillerComponentCondenserSide");
965 :
966 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
967 14321 : bool DidAnythingChange(false); // set to true if conditions changed
968 : DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
969 : Real64 Cp;
970 :
971 : // check if any conditions have changed
972 14321 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
973 :
974 14321 : if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
975 :
976 14321 : if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) DidAnythingChange = true;
977 :
978 14321 : if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) DidAnythingChange = true;
979 :
980 : // could also check heat rate against McDeltaT from node data
981 :
982 14321 : if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelCondenserHeatRate > 0.0)) {
983 :
984 : // TODO also send a request that condenser loop be made available, interlock message infrastructure??
985 :
986 0 : DidAnythingChange = true;
987 : }
988 :
989 14321 : if (DidAnythingChange || FirstHVACIteration) {
990 : // use current mass flow rate and inlet temp from Node and recalculate outlet temp
991 11487 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
992 : // update node outlet conditions
993 8718 : Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, ModelInletTemp, RoutineName);
994 8718 : state.dataLoopNodes->Node(OutletNodeNum).Temp =
995 8718 : state.dataLoopNodes->Node(InletNodeNum).Temp + ModelCondenserHeatRate / (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate * Cp);
996 : }
997 : // see 10133
998 : // state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate =
999 : // state.dataLoopNodes->Node(InletNodeNum)
1000 : // .MassFlowRate; // if condenser is on inlet branch, the outlet node needs to be updated or can get splitter/mixer failures
1001 :
1002 : // set sim flag for this loop
1003 11487 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1004 :
1005 : // set sim flag on connected loops to true because this side changed
1006 11487 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1007 34043 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1008 : // see 10133
1009 22556 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
1010 : // full chiller model is not really run when called from the condenser side and the chiller evap loop puts demand on the condenser
1011 : // loop, so this logic is flawed, remove if statement so evap side gets simulated again
1012 0 : int OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
1013 0 : OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
1014 0 : state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
1015 : }
1016 : }
1017 : }
1018 :
1019 11487 : } else { // nothing changed so turn off sim flag
1020 2834 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1021 : }
1022 14321 : }
1023 :
1024 5968 : void UpdateComponentHeatRecoverySide(EnergyPlusData &state,
1025 : int const LoopNum, // component's loop index
1026 : const DataPlant::LoopSideLocation LoopSide, // component's loop side number
1027 : [[maybe_unused]] DataPlant::PlantEquipmentType Type, // Component's type index
1028 : int const InletNodeNum, // Component's inlet node pointer
1029 : int const OutletNodeNum, // Component's outlet node pointer
1030 : Real64 const ModelRecoveryHeatRate, // model's heat rejection rate at recovery (W)
1031 : Real64 const ModelInletTemp, // model's inlet temperature (C)
1032 : Real64 const ModelOutletTemp, // model's outlet temperature (C)
1033 : Real64 const ModelMassFlowRate, // model's condenser water mass flow rate (kg/s)
1034 : bool const FirstHVACIteration)
1035 : {
1036 :
1037 : // SUBROUTINE INFORMATION:
1038 : // AUTHOR Brent Griffith
1039 : // DATE WRITTEN Sept 2010
1040 : // MODIFIED na
1041 : // RE-ENGINEERED na
1042 :
1043 : // PURPOSE OF THIS SUBROUTINE:
1044 : // provides reusable update routine for heat recovery type
1045 : // connection to plant loops
1046 :
1047 : // METHODOLOGY EMPLOYED:
1048 : // check if anything changed or doesn't agree and set simulation flags.
1049 : // update outlet conditions if needed or possible
1050 :
1051 : // SUBROUTINE PARAMETER DEFINITIONS:
1052 : static constexpr std::string_view RoutineName("UpdateComponentHeatRecoverySide");
1053 :
1054 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1055 5968 : bool DidAnythingChange(false); // set to true if conditions changed
1056 : DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
1057 : Real64 Cp; // local fluid specific heat
1058 :
1059 : // check if any conditions have changed
1060 5968 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
1061 :
1062 5968 : if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
1063 :
1064 5968 : if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) DidAnythingChange = true;
1065 :
1066 5968 : if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) DidAnythingChange = true;
1067 :
1068 : // could also check heat rate against McDeltaT from node data
1069 :
1070 5968 : if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelRecoveryHeatRate > 0.0)) {
1071 : // no flow but trying to move heat to this loop problem!
1072 :
1073 6 : DidAnythingChange = true;
1074 : }
1075 :
1076 5968 : if (DidAnythingChange || FirstHVACIteration) {
1077 : // use current mass flow rate and inlet temp from Node and recalculate outlet temp
1078 2988 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
1079 : // update node outlet conditions
1080 2976 : Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, ModelInletTemp, RoutineName);
1081 2976 : state.dataLoopNodes->Node(OutletNodeNum).Temp =
1082 2976 : state.dataLoopNodes->Node(InletNodeNum).Temp + ModelRecoveryHeatRate / (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate * Cp);
1083 : }
1084 : // see 10133
1085 : // state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate =
1086 : // state.dataLoopNodes->Node(InletNodeNum).MassFlowRate; // if heat recovery bundle is on inlet branch, the mass flow needs to be
1087 : //// updated or get splitter/mixer failures
1088 :
1089 : // set sim flag for this loop
1090 2988 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1091 :
1092 : // set sim flag on connected loops to true because this side changed
1093 2988 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1094 0 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1095 : // see 10133
1096 0 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
1097 : // full chiller model is not really run when called from the heat recovery side and the chiller evap loop puts demand on the heat
1098 : // recovery loop, so this logic is flawed, remove if statement so evap side gets simulated again
1099 0 : int OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
1100 0 : OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
1101 0 : state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
1102 : }
1103 : }
1104 : }
1105 :
1106 2988 : } else { // nothing changed so turn off sim flag
1107 2980 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1108 : }
1109 5968 : }
1110 :
1111 13877 : void UpdateAbsorberChillerComponentGeneratorSide(EnergyPlusData &state,
1112 : int const LoopNum, // component's loop index
1113 : const DataPlant::LoopSideLocation LoopSide, // component's loop side number
1114 : [[maybe_unused]] DataPlant::PlantEquipmentType const Type, // Component's type index
1115 : int const InletNodeNum, // Component's inlet node pointer
1116 : [[maybe_unused]] int const OutletNodeNum, // Component's outlet node pointer
1117 : [[maybe_unused]] DataLoopNode::NodeFluidType const HeatSourceType, // Type of fluid in Generator loop
1118 : Real64 const ModelGeneratorHeatRate, // model's generator heat rate (W)
1119 : Real64 const ModelMassFlowRate, // model's generator mass flow rate (kg/s)
1120 : bool const FirstHVACIteration)
1121 : {
1122 :
1123 : // SUBROUTINE INFORMATION:
1124 : // AUTHOR Brent Griffith
1125 : // DATE WRITTEN February 2010
1126 : // MODIFIED na
1127 : // RE-ENGINEERED na
1128 :
1129 : // PURPOSE OF THIS SUBROUTINE:
1130 : // provides reusable update routine for absoption chiller's generator
1131 : // connection to plant loops
1132 :
1133 : // METHODOLOGY EMPLOYED:
1134 : // check if anything changed or doesn't agree and set simulation flags.
1135 : // update outlet conditions if needed or possible
1136 :
1137 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1138 13877 : bool DidAnythingChange(false); // set to true if conditions changed
1139 : DataPlant::LoopSideLocation OtherLoopSide; // local loop side pointer for remote connected loop
1140 :
1141 : // check if any conditions have changed
1142 13877 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) DidAnythingChange = true;
1143 :
1144 13877 : if ((state.dataLoopNodes->Node(InletNodeNum).MassFlowRate == 0.0) && (ModelGeneratorHeatRate > 0.0)) {
1145 :
1146 : // TODO also send a request that generator loop be made available, interlock message infrastructure??
1147 :
1148 0 : DidAnythingChange = true;
1149 : }
1150 :
1151 13877 : if (DidAnythingChange || FirstHVACIteration) {
1152 :
1153 : // set sim flag for this loop
1154 11149 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1155 :
1156 : // set sim flag on connected loops to true because this side changed
1157 11149 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1158 33447 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1159 22298 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
1160 11149 : int OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
1161 11149 : OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
1162 11149 : state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
1163 : }
1164 : }
1165 : }
1166 :
1167 11149 : } else { // nothing changed so turn off sim flag
1168 2728 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1169 : }
1170 13877 : }
1171 :
1172 46 : void InterConnectTwoPlantLoopSides(EnergyPlusData &state,
1173 : PlantLocation const &Loop1PlantLoc,
1174 : PlantLocation const &Loop2PlantLoc,
1175 : DataPlant::PlantEquipmentType ComponentType,
1176 : bool const Loop1DemandsOnLoop2)
1177 : {
1178 :
1179 : // SUBROUTINE INFORMATION:
1180 : // AUTHOR B. Griffith
1181 : // DATE WRITTEN February 2010
1182 : // MODIFIED na
1183 : // RE-ENGINEERED na
1184 :
1185 : // PURPOSE OF THIS SUBROUTINE:
1186 : // Setup PlantLoop data structure pointers to direct interacting loops
1187 :
1188 : // Using/Aliasing
1189 : using DataPlant::ConnectedLoopData;
1190 :
1191 46 : if (Loop1PlantLoc.loopNum == 0 || Loop1PlantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid || Loop2PlantLoc.loopNum == 0 ||
1192 46 : Loop2PlantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
1193 0 : return; // Associated ScanPlantLoopsForObject couldn't find the component in the the plant loop structure...
1194 : } // This is a Fatal error condition
1195 :
1196 46 : bool const Loop2DemandsOnLoop1(!Loop1DemandsOnLoop2);
1197 :
1198 : int TotalConnected;
1199 :
1200 46 : auto &loop_side_1 = state.dataPlnt->PlantLoop(Loop1PlantLoc.loopNum).LoopSide(Loop1PlantLoc.loopSideNum);
1201 46 : auto &connected_1 = loop_side_1.Connected;
1202 46 : if (allocated(connected_1)) {
1203 10 : TotalConnected = ++loop_side_1.TotalConnected;
1204 10 : connected_1.redimension(TotalConnected);
1205 : } else {
1206 36 : TotalConnected = loop_side_1.TotalConnected = 1;
1207 36 : connected_1.allocate(1);
1208 : }
1209 46 : connected_1(TotalConnected).LoopNum = Loop2PlantLoc.loopNum;
1210 46 : connected_1(TotalConnected).LoopSideNum = Loop2PlantLoc.loopSideNum;
1211 46 : connected_1(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
1212 46 : connected_1(TotalConnected).LoopDemandsOnRemote = Loop1DemandsOnLoop2;
1213 :
1214 46 : auto &loop_side_2 = state.dataPlnt->PlantLoop(Loop2PlantLoc.loopNum).LoopSide(Loop2PlantLoc.loopSideNum);
1215 46 : auto &connected_2 = loop_side_2.Connected;
1216 46 : if (allocated(connected_2)) {
1217 8 : TotalConnected = ++loop_side_2.TotalConnected;
1218 8 : connected_2.redimension(TotalConnected);
1219 : } else {
1220 38 : TotalConnected = loop_side_2.TotalConnected = 1;
1221 38 : connected_2.allocate(1);
1222 : }
1223 46 : connected_2(TotalConnected).LoopNum = Loop1PlantLoc.loopNum;
1224 46 : connected_2(TotalConnected).LoopSideNum = Loop1PlantLoc.loopSideNum;
1225 46 : connected_2(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
1226 46 : connected_2(TotalConnected).LoopDemandsOnRemote = Loop2DemandsOnLoop1;
1227 : }
1228 :
1229 13 : void ShiftPlantLoopSideCallingOrder(EnergyPlusData &state, int const OldIndex, int const NewIndex)
1230 : {
1231 :
1232 : // SUBROUTINE INFORMATION:
1233 : // AUTHOR B. Griffith
1234 : // DATE WRITTEN <April 2011
1235 : // MODIFIED na
1236 : // RE-ENGINEERED na
1237 :
1238 : // PURPOSE OF THIS SUBROUTINE:
1239 : // re-arrange the calling order, move one loop side from an old index to a new one
1240 :
1241 : // Using/Aliasing
1242 : using namespace DataPlant;
1243 :
1244 : // Object Data
1245 13 : PlantCallingOrderInfoStruct RecordToMoveInPlantCallingOrderInfo;
1246 :
1247 13 : if (OldIndex == 0) {
1248 0 : ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, Old Index=0");
1249 : }
1250 13 : if (NewIndex == 0) {
1251 0 : ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, New Index=1");
1252 : }
1253 13 : if ((OldIndex == 0) || (NewIndex == 0)) {
1254 0 : return;
1255 : }
1256 :
1257 : // store copy of prior structure
1258 13 : Array1D<PlantCallingOrderInfoStruct> TempPlantCallingOrderInfo(state.dataPlnt->PlantCallingOrderInfo);
1259 :
1260 13 : RecordToMoveInPlantCallingOrderInfo = state.dataPlnt->PlantCallingOrderInfo(OldIndex);
1261 :
1262 13 : if (OldIndex == NewIndex) {
1263 : // do nothing, no shift needed.
1264 11 : } else if ((OldIndex == 1) && (NewIndex > OldIndex) && (NewIndex < state.dataPlnt->TotNumHalfLoops)) {
1265 : // example was: 1 2 3 4 5 6 7 8 (with OI = 1, NI = 5)
1266 : // example shifted: 2 3 4 5 1 6 7 8
1267 :
1268 0 : state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({2, NewIndex});
1269 0 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1270 0 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1271 0 : TempPlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops});
1272 :
1273 11 : } else if ((OldIndex == 1) && (NewIndex > OldIndex) && (NewIndex == state.dataPlnt->TotNumHalfLoops)) {
1274 : // example was: 1 2 3 4 5 6 7 8 (with OI = 1, NI = 8)
1275 : // example shifted: 2 3 4 5 6 7 8 1
1276 :
1277 0 : state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({2, NewIndex});
1278 0 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1279 11 : } else if ((OldIndex > 1) && (NewIndex > OldIndex) && (NewIndex < state.dataPlnt->TotNumHalfLoops)) {
1280 : // example was: 1 2 3 4 5 6 7 8 (with OI = 3, NI = 6)
1281 : // example shifted: 1 2 4 5 6 3 7 8
1282 0 : state.dataPlnt->PlantCallingOrderInfo({1, OldIndex - 1}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
1283 0 : state.dataPlnt->PlantCallingOrderInfo({OldIndex, NewIndex - 1}) = TempPlantCallingOrderInfo({OldIndex + 1, NewIndex});
1284 0 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1285 0 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1286 0 : TempPlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops});
1287 11 : } else if ((OldIndex > 1) && (NewIndex > OldIndex) && (NewIndex == state.dataPlnt->TotNumHalfLoops)) {
1288 : // example was: 1 2 3 4 5 6 7 8 (with OI = 3, NI = 8)
1289 : // example shifted: 1 2 4 5 6 7 8 3
1290 0 : state.dataPlnt->PlantCallingOrderInfo({1, OldIndex - 1}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
1291 0 : state.dataPlnt->PlantCallingOrderInfo({OldIndex, NewIndex - 1}) = TempPlantCallingOrderInfo({OldIndex + 1, NewIndex});
1292 0 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1293 11 : } else if ((OldIndex > 1) && (NewIndex < OldIndex) && (NewIndex == 1)) {
1294 : // example was: 1 2 3 4 5 6 7 8 (with OI = 3, NI = 1)
1295 : // example shifted: 3 1 2 4 5 6 7 8
1296 2 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1297 2 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
1298 4 : state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1299 6 : TempPlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops});
1300 :
1301 9 : } else if ((OldIndex > 1) && (NewIndex < OldIndex) && (NewIndex > 1)) {
1302 : // example was: 1 2 3 4 5 6 7 8 (with OI = 3, NI = 2)
1303 : // example shifted: 1 3 2 4 5 6 7 8
1304 9 : state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({1, NewIndex - 1});
1305 9 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1306 9 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({NewIndex, NewIndex + (OldIndex - NewIndex) - 1});
1307 18 : state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1308 27 : TempPlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops});
1309 :
1310 : } else {
1311 0 : ShowSevereError(state,
1312 : "ShiftPlantLoopSideCallingOrder: developer error notice, caught unexpected logical case in "
1313 : "ShiftPlantLoopSideCallingOrder PlantUtilities");
1314 : }
1315 13 : }
1316 :
1317 508 : void RegisterPlantCompDesignFlow(EnergyPlusData &state,
1318 : int const ComponentInletNodeNum, // the component's water inlet node number
1319 : Real64 const DesPlantFlow // the component's design fluid volume flow rate [m3/s]
1320 : )
1321 : {
1322 :
1323 : // SUBROUTINE INFORMATION:
1324 : // AUTHOR Fred Buhl(previously SaveCompDesWaterFlow in General.cc)
1325 : // DATE WRITTEN January 2004
1326 : // MODIFIED
1327 : // RE-ENGINEERED B. Griffith April 2011, allow to enter repeatedly
1328 :
1329 : // PURPOSE OF THIS SUBROUTINE:
1330 : // Regester the design fluid flow rates of plant components for sizing purposes
1331 : // in an array that can be accessed by the plant manager routines
1332 : // allows sizing routines to iterate by safely processing repeated calls from the same component
1333 :
1334 : // METHODOLOGY EMPLOYED:
1335 : // Derived from SaveCompDesWaterFlow but changed to allow re entry with the same node just update
1336 : // the information at the same location in the structure
1337 : // The design flow rate is stored in a dynamic structure array along with the plant component's inlet node number
1338 : // (which is used by plant as a component identifier instead if name and type).
1339 :
1340 : // Using/Aliasing
1341 : using namespace DataSizing;
1342 :
1343 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1344 : int NumPlantComps;
1345 : int PlantCompNum; // component do loop index
1346 : bool Found;
1347 : int thisCallNodeIndex;
1348 :
1349 508 : NumPlantComps = state.dataSize->SaveNumPlantComps;
1350 :
1351 508 : if (NumPlantComps == 0) { // first time in, fill and return
1352 151 : NumPlantComps = 1;
1353 151 : state.dataSize->CompDesWaterFlow.allocate(NumPlantComps);
1354 : // save the new data
1355 151 : state.dataSize->CompDesWaterFlow(NumPlantComps).SupNode = ComponentInletNodeNum;
1356 151 : state.dataSize->CompDesWaterFlow(NumPlantComps).DesVolFlowRate = DesPlantFlow;
1357 151 : state.dataSize->SaveNumPlantComps = NumPlantComps;
1358 151 : return;
1359 : }
1360 :
1361 357 : Found = false;
1362 : // find node num index in structure if any
1363 1308 : for (PlantCompNum = 1; PlantCompNum <= NumPlantComps; ++PlantCompNum) {
1364 1171 : if (ComponentInletNodeNum == state.dataSize->CompDesWaterFlow(PlantCompNum).SupNode) {
1365 220 : Found = true;
1366 220 : thisCallNodeIndex = PlantCompNum;
1367 : }
1368 1171 : if (Found) break;
1369 : }
1370 :
1371 357 : if (!Found) { // grow structure and add new node at the end
1372 137 : ++NumPlantComps; // increment the number of components that use water as a source of heat or coolth
1373 137 : state.dataSize->CompDesWaterFlow.emplace_back(ComponentInletNodeNum, DesPlantFlow); // Append the new element
1374 137 : state.dataSize->SaveNumPlantComps = NumPlantComps;
1375 : } else {
1376 220 : state.dataSize->CompDesWaterFlow(thisCallNodeIndex).SupNode = ComponentInletNodeNum;
1377 220 : state.dataSize->CompDesWaterFlow(thisCallNodeIndex).DesVolFlowRate = DesPlantFlow;
1378 : }
1379 : }
1380 :
1381 1116784 : void SafeCopyPlantNode(EnergyPlusData &state,
1382 : int const InletNodeNum,
1383 : int const OutletNodeNum,
1384 : ObjexxFCL::Optional_int_const LoopNum,
1385 : [[maybe_unused]] ObjexxFCL::Optional<Real64 const> OutletTemp // set on outlet node if present and water.
1386 : )
1387 : {
1388 :
1389 : // SUBROUTINE INFORMATION:
1390 : // AUTHOR B. Griffith
1391 : // DATE WRITTEN February, 2010
1392 : // MODIFIED na
1393 : // RE-ENGINEERED na
1394 :
1395 : // PURPOSE OF THIS SUBROUTINE:
1396 : // Provide a safer alternative for Node(outlet) = Node(inlet)
1397 : // Intended just for plant
1398 :
1399 : // METHODOLOGY EMPLOYED:
1400 : // Copy over state variables but not setpoints
1401 : // derived from adiabatic Pipes
1402 :
1403 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1404 1116784 : state.dataLoopNodes->Node(OutletNodeNum).FluidType = state.dataLoopNodes->Node(InletNodeNum).FluidType;
1405 :
1406 1116784 : state.dataLoopNodes->Node(OutletNodeNum).Temp = state.dataLoopNodes->Node(InletNodeNum).Temp;
1407 1116784 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
1408 1116784 : state.dataLoopNodes->Node(OutletNodeNum).Quality = state.dataLoopNodes->Node(InletNodeNum).Quality;
1409 1116784 : state.dataLoopNodes->Node(OutletNodeNum).Enthalpy =
1410 1116784 : state.dataLoopNodes->Node(InletNodeNum).Enthalpy; // should have routines that keep this current with temp?
1411 :
1412 1116784 : state.dataLoopNodes->Node(OutletNodeNum).TempMin = state.dataLoopNodes->Node(InletNodeNum).TempMin;
1413 1116784 : state.dataLoopNodes->Node(OutletNodeNum).TempMax = state.dataLoopNodes->Node(InletNodeNum).TempMax;
1414 1116784 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMinAvail =
1415 1116784 : max(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMin, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMinAvail);
1416 1116784 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMaxAvail =
1417 1116784 : min(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMax, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMaxAvail);
1418 :
1419 1116784 : state.dataLoopNodes->Node(OutletNodeNum).HumRat = state.dataLoopNodes->Node(InletNodeNum).HumRat; // air only?
1420 :
1421 : // Only pass pressure if we aren't doing a pressure simulation
1422 1116784 : if (present(LoopNum)) {
1423 836189 : switch (state.dataPlnt->PlantLoop(LoopNum).PressureSimType) {
1424 836189 : case DataPlant::PressSimType::NoPressure:
1425 836189 : state.dataLoopNodes->Node(OutletNodeNum).Press = state.dataLoopNodes->Node(InletNodeNum).Press;
1426 836189 : default:
1427 : // Don't do anything
1428 836189 : break;
1429 : }
1430 : }
1431 1116784 : }
1432 :
1433 662407 : Real64 BoundValueToNodeMinMaxAvail(EnergyPlusData &state, Real64 const ValueToBound, int const NodeNumToBoundWith)
1434 : {
1435 :
1436 : // FUNCTION INFORMATION:
1437 : // AUTHOR Edwin Lee
1438 : // DATE WRITTEN September 2010
1439 : // MODIFIED na
1440 : // RE-ENGINEERED na
1441 :
1442 : // PURPOSE OF THIS FUNCTION:
1443 : // Provides a clean way to quickly bound a generic value to within any node's minavail and maxavail range
1444 :
1445 : // METHODOLOGY EMPLOYED:
1446 : // Bound up to min avail, down to max avail
1447 :
1448 : // Return value
1449 : Real64 BoundedValue;
1450 :
1451 662407 : BoundedValue = ValueToBound;
1452 662407 : BoundedValue = max(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMinAvail);
1453 662407 : BoundedValue = min(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMaxAvail);
1454 :
1455 662407 : return BoundedValue;
1456 : }
1457 :
1458 88208 : void TightenNodeMinMaxAvails(EnergyPlusData &state, int const NodeNum, Real64 const NewMinAvail, Real64 const NewMaxAvail)
1459 : {
1460 :
1461 : // SUBROUTINE INFORMATION:
1462 : // AUTHOR Edwin Lee
1463 : // DATE WRITTEN January, 2011
1464 : // MODIFIED na
1465 : // RE-ENGINEERED na
1466 :
1467 : // PURPOSE OF THIS SUBROUTINE:
1468 : // Provides a means of tightening up min/max avail on a node if possible
1469 :
1470 : // METHODOLOGY EMPLOYED:
1471 : // Bring up node min avail to new min avail if it doesn't violate any other node conditions
1472 : // Pull down node max avail to new max avail if it doesn't violate any other node conditions
1473 : // Assumes that current min/max avails are already honoring hardware min/max values, so they aren't checked here
1474 :
1475 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1476 : Real64 OldMinAvail;
1477 : Real64 OldMaxAvail;
1478 :
1479 88208 : OldMinAvail = state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail;
1480 88208 : OldMaxAvail = state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail;
1481 :
1482 : // If the new min avail is higher than previous, and it isn't higher than the max avail, update MIN AVAIL
1483 88208 : if ((NewMinAvail > OldMinAvail) && (NewMinAvail <= OldMaxAvail)) state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail = NewMinAvail;
1484 :
1485 : // If the new max avail is lower than previous, and it isn't lower than the min avail, update MAX AVAIL
1486 88208 : if ((NewMaxAvail < OldMaxAvail) && (NewMaxAvail >= OldMinAvail)) state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = NewMaxAvail;
1487 88208 : }
1488 :
1489 88208 : Real64 BoundValueToWithinTwoValues(Real64 const ValueToBound, Real64 const LowerBound, Real64 const UpperBound)
1490 : {
1491 :
1492 : // FUNCTION INFORMATION:
1493 : // AUTHOR Edwin Lee
1494 : // DATE WRITTEN September 2010
1495 : // MODIFIED na
1496 : // RE-ENGINEERED na
1497 :
1498 : // PURPOSE OF THIS FUNCTION:
1499 : // Provides a clean way to quickly bound a generic value to within any two other values
1500 :
1501 : // METHODOLOGY EMPLOYED:
1502 : // Bound up to min and down to max
1503 :
1504 : // Return value
1505 : Real64 BoundedValue;
1506 :
1507 88208 : BoundedValue = ValueToBound;
1508 88208 : BoundedValue = max(BoundedValue, LowerBound);
1509 88208 : BoundedValue = min(BoundedValue, UpperBound);
1510 :
1511 88208 : return BoundedValue;
1512 : }
1513 :
1514 5 : bool IntegerIsWithinTwoValues(int const ValueToCheck, int const LowerBound, int const UpperBound)
1515 : {
1516 :
1517 : // FUNCTION INFORMATION:
1518 : // AUTHOR Edwin Lee
1519 : // DATE WRITTEN September 2010
1520 : // MODIFIED na
1521 : // RE-ENGINEERED na
1522 :
1523 : // PURPOSE OF THIS FUNCTION:
1524 : // Provides a clean way to quickly check if an integer is within two values
1525 :
1526 : // METHODOLOGY EMPLOYED:
1527 : // TRUE if ValueToCheck = [LowerBound, UpperBound]
1528 : // in other words, it returns true if ValueToCheck=LowerBound, or if ValueToCheck=UpperBound
1529 :
1530 : // Return value
1531 5 : return (ValueToCheck >= LowerBound) && (ValueToCheck <= UpperBound);
1532 : }
1533 :
1534 : // In-Place Right Shift by 1 of Array Elements
1535 343744 : void rshift1(Array1D<Real64> &a, Real64 const a_l)
1536 : {
1537 343744 : assert(a.size_bounded());
1538 1718720 : for (int i = a.u(), e = a.l(); i > e; --i) {
1539 1374976 : a(i) = a(i - 1);
1540 : }
1541 343744 : a(a.l()) = a_l;
1542 343744 : }
1543 :
1544 24886 : void LogPlantConvergencePoints(EnergyPlusData &state, bool const FirstHVACIteration)
1545 : {
1546 :
1547 : // SUBROUTINE INFORMATION:
1548 : // AUTHOR Edwin Lee
1549 : // DATE WRITTEN Summer 2011
1550 : // MODIFIED na
1551 : // RE-ENGINEERED na
1552 :
1553 : // PURPOSE OF THIS SUBROUTINE:
1554 : // This routine stores the history of the plant convergence to check for stuck (max iteration) conditions
1555 :
1556 : // METHODOLOGY EMPLOYED:
1557 : // Loop across all loops and loopsides
1558 : // On first hvac, reset the history arrays to begin anew
1559 : // Pick up the LoopSide inlet and outlet temp and flow rate
1560 : // Store this in the history array of each node using EOSHIFT
1561 :
1562 67854 : for (int ThisLoopNum = 1; ThisLoopNum <= isize(state.dataPlnt->PlantLoop); ++ThisLoopNum) {
1563 42968 : auto &loop = state.dataPlnt->PlantLoop(ThisLoopNum);
1564 128904 : for (DataPlant::LoopSideLocation ThisLoopSide : DataPlant::LoopSideKeys) {
1565 85936 : auto &loop_side = loop.LoopSide(ThisLoopSide);
1566 :
1567 85936 : if (FirstHVACIteration) {
1568 42948 : loop_side.InletNode.TemperatureHistory = 0.0;
1569 42948 : loop_side.InletNode.MassFlowRateHistory = 0.0;
1570 42948 : loop_side.OutletNode.TemperatureHistory = 0.0;
1571 42948 : loop_side.OutletNode.MassFlowRateHistory = 0.0;
1572 : }
1573 :
1574 85936 : int InletNodeNum = loop_side.NodeNumIn;
1575 85936 : Real64 InletNodeTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
1576 85936 : Real64 InletNodeMdot = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
1577 :
1578 85936 : int OutletNodeNum = loop_side.NodeNumOut;
1579 85936 : Real64 OutletNodeTemp = state.dataLoopNodes->Node(OutletNodeNum).Temp;
1580 85936 : Real64 OutletNodeMdot = state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate;
1581 :
1582 85936 : rshift1(loop_side.InletNode.TemperatureHistory, InletNodeTemp);
1583 85936 : rshift1(loop_side.InletNode.MassFlowRateHistory, InletNodeMdot);
1584 85936 : rshift1(loop_side.OutletNode.TemperatureHistory, OutletNodeTemp);
1585 85936 : rshift1(loop_side.OutletNode.MassFlowRateHistory, OutletNodeMdot);
1586 : }
1587 : }
1588 24886 : }
1589 :
1590 695 : void ScanPlantLoopsForObject(EnergyPlusData &state,
1591 : std::string_view CompName,
1592 : DataPlant::PlantEquipmentType CompType,
1593 : PlantLocation &plantLoc,
1594 : bool &errFlag,
1595 : ObjexxFCL::Optional<Real64 const> LowLimitTemp,
1596 : ObjexxFCL::Optional<Real64 const> HighLimitTemp,
1597 : ObjexxFCL::Optional_int CountMatchPlantLoops,
1598 : ObjexxFCL::Optional_int_const InletNodeNumber,
1599 : ObjexxFCL::Optional_int_const SingleLoopSearch,
1600 : ObjexxFCL::Optional_bool_const suppressErrors)
1601 : {
1602 :
1603 : // SUBROUTINE INFORMATION:
1604 : // AUTHOR Edwin Lee
1605 : // DATE WRITTEN November 2009
1606 : // MODIFIED B. Griffith, changes to help with single component one multiple plant loops
1607 : // RE-ENGINEERED na
1608 : // PURPOSE OF THIS SUBROUTINE:
1609 : // This subroutine scans the plant loop structure trying to find the component by type then name.
1610 : // If there are more than one match, it counts them up and returns count using an optional output arg
1611 : // If the option input declaring the component inlet's node name, then the matching is more specific.
1612 : // An optional input, lowlimittemp, can be passed in to be used in the PlantCondLoopOperation routines
1613 : // when distributing loads to components
1614 : // METHODOLOGY EMPLOYED:
1615 : // Standard EnergyPlus methodology.
1616 :
1617 : // Using/Aliasing
1618 : using BranchInputManager::AuditBranches;
1619 :
1620 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1621 : int LoopCtr;
1622 : int BranchCtr;
1623 : int CompCtr;
1624 : bool FoundComponent;
1625 : int FoundCount;
1626 : bool FoundCompName;
1627 : int StartingLoopNum;
1628 : int EndingLoopNum;
1629 :
1630 695 : FoundCount = 0;
1631 :
1632 695 : FoundComponent = false;
1633 695 : FoundCompName = false;
1634 695 : StartingLoopNum = 1;
1635 695 : EndingLoopNum = state.dataPlnt->TotNumLoops;
1636 695 : if (present(SingleLoopSearch)) {
1637 37 : StartingLoopNum = SingleLoopSearch;
1638 37 : EndingLoopNum = SingleLoopSearch;
1639 : }
1640 :
1641 1964 : for (LoopCtr = StartingLoopNum; LoopCtr <= EndingLoopNum; ++LoopCtr) {
1642 1269 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1643 3807 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1644 2538 : auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
1645 10279 : for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
1646 7741 : auto &this_branch = this_loop_side.Branch(BranchCtr);
1647 15539 : for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
1648 7798 : auto &this_component = this_branch.Comp(CompCtr);
1649 7798 : if (this_component.Type == CompType) {
1650 2932 : if (Util::SameString(CompName, this_component.Name)) {
1651 804 : FoundCompName = true;
1652 804 : if (present(InletNodeNumber)) {
1653 231 : if (InletNodeNumber > 0) {
1654 : // check if inlet nodes agree
1655 231 : if (InletNodeNumber == this_component.NodeNumIn) {
1656 117 : FoundComponent = true;
1657 117 : ++FoundCount;
1658 117 : plantLoc.loopNum = LoopCtr;
1659 117 : plantLoc.loopSideNum = LoopSideCtr;
1660 117 : plantLoc.branchNum = BranchCtr;
1661 117 : plantLoc.compNum = CompCtr;
1662 :
1663 117 : plantLoc.loop = &this_loop;
1664 117 : plantLoc.side = &this_loop_side;
1665 117 : plantLoc.branch = &this_branch;
1666 117 : plantLoc.comp = &this_component;
1667 : }
1668 : }
1669 : } else {
1670 573 : FoundComponent = true;
1671 573 : ++FoundCount;
1672 573 : plantLoc.loopNum = LoopCtr;
1673 573 : plantLoc.loopSideNum = LoopSideCtr;
1674 573 : plantLoc.branchNum = BranchCtr;
1675 573 : plantLoc.compNum = CompCtr;
1676 :
1677 573 : plantLoc.loop = &this_loop;
1678 573 : plantLoc.side = &this_loop_side;
1679 573 : plantLoc.branch = &this_branch;
1680 573 : plantLoc.comp = &this_component;
1681 : }
1682 804 : if (present(LowLimitTemp)) {
1683 23 : this_component.MinOutletTemp = LowLimitTemp;
1684 : }
1685 804 : if (present(HighLimitTemp)) {
1686 3 : this_component.MaxOutletTemp = HighLimitTemp;
1687 : }
1688 : }
1689 : }
1690 : }
1691 : }
1692 : }
1693 : }
1694 :
1695 695 : bool skipErrors = false;
1696 695 : if (present(suppressErrors)) {
1697 0 : skipErrors = suppressErrors;
1698 : }
1699 :
1700 695 : if (!FoundComponent && !skipErrors) {
1701 5 : if (CompType != DataPlant::PlantEquipmentType::Invalid && CompType != DataPlant::PlantEquipmentType::Num) {
1702 5 : if (!present(SingleLoopSearch)) {
1703 10 : ShowSevereError(state,
1704 10 : format("Plant Component {} called \"{}\" was not found on any plant loops.",
1705 5 : DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)],
1706 10 : std::string{CompName}));
1707 5 : AuditBranches(state, true, DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)], CompName);
1708 : } else {
1709 0 : ShowSevereError(state,
1710 0 : format("Plant Component {} called \"{}\" was not found on plant loop=\"{}\".",
1711 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)],
1712 0 : std::string{CompName},
1713 0 : state.dataPlnt->PlantLoop(SingleLoopSearch).Name));
1714 : }
1715 5 : if (present(InletNodeNumber)) {
1716 4 : if (FoundCompName) {
1717 4 : ShowContinueError(state, format("Looking for matching inlet Node=\"{}\".", state.dataLoopNodes->NodeID(InletNodeNumber)));
1718 : }
1719 : }
1720 5 : if (present(SingleLoopSearch)) {
1721 0 : ShowContinueError(state, format("Look at Operation Scheme=\"{}\".", state.dataPlnt->PlantLoop(SingleLoopSearch).OperationScheme));
1722 0 : ShowContinueError(state, "Look at Branches and Components on the Loop.");
1723 0 : ShowBranchesOnLoop(state, SingleLoopSearch);
1724 : }
1725 5 : errFlag = true;
1726 : } else {
1727 0 : ShowSevereError(state, format("ScanPlantLoopsForObject: Invalid CompType passed [{}], Name={}", CompType, CompName));
1728 0 : ShowContinueError(state, format("Valid CompTypes are in the range [0 - {}].", static_cast<int>(DataPlant::PlantEquipmentType::Num)));
1729 0 : ShowFatalError(state, "Previous error causes program termination");
1730 : }
1731 : }
1732 :
1733 695 : if (present(CountMatchPlantLoops)) {
1734 317 : CountMatchPlantLoops = FoundCount;
1735 : }
1736 695 : }
1737 :
1738 189 : void ScanPlantLoopsForNodeNum(EnergyPlusData &state,
1739 : std::string_view const CallerName, // really used for error messages
1740 : int const NodeNum, // index in Node structure of node to be scanned
1741 : PlantLocation &plantLoc, // return value for location
1742 : ObjexxFCL::Optional_int CompNum)
1743 : {
1744 :
1745 : // SUBROUTINE INFORMATION:
1746 : // AUTHOR B. Griffith
1747 : // DATE WRITTEN Feb. 2010
1748 : // MODIFIED na
1749 : // RE-ENGINEERED na
1750 :
1751 : // PURPOSE OF THIS SUBROUTINE:
1752 : // Get routine to return plant loop index and plant loop side
1753 : // based on node number. for one time init routines only.
1754 :
1755 : // METHODOLOGY EMPLOYED:
1756 : // Loop thru plant data structure and find matching node.
1757 :
1758 : int LoopCtr;
1759 : int BranchCtr;
1760 : int CompCtr;
1761 : bool FoundNode;
1762 : int inFoundCount;
1763 : int outFoundCount;
1764 :
1765 189 : inFoundCount = 0;
1766 189 : outFoundCount = 0;
1767 189 : if (present(CompNum)) {
1768 2 : CompNum = 0;
1769 : }
1770 189 : FoundNode = false;
1771 :
1772 526 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1773 337 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1774 1011 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1775 674 : auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
1776 2736 : for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
1777 2062 : auto &this_branch = this_loop_side.Branch(BranchCtr);
1778 4153 : for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
1779 2091 : auto &this_comp = this_branch.Comp(CompCtr);
1780 2091 : if (NodeNum == this_comp.NodeNumIn) {
1781 190 : FoundNode = true;
1782 190 : ++inFoundCount;
1783 190 : plantLoc.loopNum = LoopCtr;
1784 190 : plantLoc.loopSideNum = LoopSideCtr;
1785 190 : plantLoc.branchNum = BranchCtr;
1786 :
1787 190 : if (present(CompNum)) {
1788 3 : CompNum = CompCtr; // What is this? This is an input
1789 : }
1790 190 : plantLoc.loop = &this_loop;
1791 190 : plantLoc.side = &this_loop_side;
1792 190 : plantLoc.branch = &this_branch;
1793 190 : plantLoc.comp = &this_comp;
1794 : }
1795 :
1796 2091 : if (NodeNum == this_comp.NodeNumOut) {
1797 5 : ++outFoundCount;
1798 5 : plantLoc.loopNum = LoopCtr;
1799 5 : plantLoc.loopSideNum = LoopSideCtr;
1800 5 : plantLoc.branchNum = BranchCtr;
1801 :
1802 5 : plantLoc.loop = &this_loop;
1803 5 : plantLoc.side = &this_loop_side;
1804 5 : plantLoc.branch = &this_branch;
1805 5 : plantLoc.comp = &this_comp;
1806 : }
1807 : }
1808 : }
1809 : }
1810 : }
1811 :
1812 189 : if (!FoundNode) {
1813 0 : ShowSevereError(state, "ScanPlantLoopsForNodeNum: Plant Node was not found as inlet node (for component) on any plant loops");
1814 0 : ShowContinueError(state, format("Node Name=\"{}\"", state.dataLoopNodes->NodeID(NodeNum)));
1815 0 : if (!state.dataGlobal->DoingSizing) {
1816 0 : ShowContinueError(state, format("called by {}", CallerName));
1817 : } else {
1818 0 : ShowContinueError(state, format("during sizing: called by {}", CallerName));
1819 : }
1820 0 : if (outFoundCount > 0) ShowContinueError(state, format("Node was found as outlet node (for component) {} time(s).", outFoundCount));
1821 0 : ShowContinueError(state, "Possible error in Branch inputs. For more information, look for other error messages related to this node name.");
1822 : // fatal?
1823 : }
1824 189 : }
1825 :
1826 : // Utility function, mostly for unit tests.
1827 567 : void SetPlantLocationLinks(EnergyPlusData &state, PlantLocation &plantLoc)
1828 : {
1829 567 : if (plantLoc.loopNum == 0) return;
1830 566 : plantLoc.loop = &state.dataPlnt->PlantLoop(plantLoc.loopNum);
1831 566 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) return;
1832 542 : plantLoc.side = &plantLoc.loop->LoopSide(plantLoc.loopSideNum);
1833 542 : if (plantLoc.branchNum == 0) return;
1834 541 : plantLoc.branch = &plantLoc.side->Branch(plantLoc.branchNum);
1835 541 : if (plantLoc.compNum == 0) return;
1836 541 : plantLoc.comp = &plantLoc.branch->Comp(plantLoc.compNum);
1837 : }
1838 :
1839 7875 : bool AnyPlantLoopSidesNeedSim(EnergyPlusData &state)
1840 : {
1841 :
1842 : // FUNCTION INFORMATION:
1843 : // AUTHOR Edwin Lee
1844 : // DATE WRITTEN November 2009
1845 : // MODIFIED na
1846 : // RE-ENGINEERED na
1847 : // PURPOSE OF THIS FUNCTION:
1848 : // This subroutine scans the plant LoopSide simflags and returns if any of them are still true
1849 :
1850 : // Return value
1851 : bool AnyPlantLoopSidesNeedSim;
1852 :
1853 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1854 : int LoopCtr;
1855 :
1856 : // Assume that there aren't any
1857 7875 : AnyPlantLoopSidesNeedSim = false;
1858 :
1859 : // Then check if there are any
1860 7878 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1861 10 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1862 7 : if (state.dataPlnt->PlantLoop(LoopCtr).LoopSide(LoopSideCtr).SimLoopSideNeeded) {
1863 1 : AnyPlantLoopSidesNeedSim = true;
1864 1 : return AnyPlantLoopSidesNeedSim;
1865 : }
1866 : }
1867 : }
1868 :
1869 7874 : return AnyPlantLoopSidesNeedSim;
1870 : }
1871 :
1872 505709 : void SetAllPlantSimFlagsToValue(EnergyPlusData &state, bool const Value)
1873 : {
1874 :
1875 : // SUBROUTINE INFORMATION:
1876 : // AUTHOR Edwin Lee
1877 : // DATE WRITTEN November 2009
1878 : // MODIFIED na
1879 : // RE-ENGINEERED B. Griffith Feb 2009
1880 : // PURPOSE OF THIS SUBROUTINE:
1881 : // Quickly sets all sim flags of a certain type (loop type/side) to a value
1882 :
1883 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1884 : int LoopCtr;
1885 :
1886 : // Loop over all loops
1887 576573 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1888 70864 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1889 70864 : this_loop.LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = Value;
1890 70864 : this_loop.LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = Value;
1891 : }
1892 505709 : }
1893 :
1894 0 : void ShowBranchesOnLoop(EnergyPlusData &state, int const LoopNum) // Loop number of loop
1895 : {
1896 :
1897 : // SUBROUTINE INFORMATION:
1898 : // AUTHOR Linda Lawrie
1899 : // DATE WRITTEN November 2011
1900 : // MODIFIED na
1901 : // RE-ENGINEERED na
1902 :
1903 : // PURPOSE OF THIS SUBROUTINE:
1904 : // This routine will display (with continue error messages) the branch/component
1905 : // structure of the given loop.
1906 :
1907 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1908 : int BrN; // Branch counter
1909 : int CpN; // Component (on branch) counter
1910 :
1911 0 : for (DataPlant::LoopSideLocation LSN : DataPlant::LoopSideKeys) {
1912 0 : ShowContinueError(state, format("{} Branches:", DataPlant::DemandSupplyNames[static_cast<int>(LSN)]));
1913 0 : for (BrN = 1; BrN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).TotalBranches; ++BrN) {
1914 0 : ShowContinueError(state, format(" {}", state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Name));
1915 0 : ShowContinueError(state, " Components on Branch:");
1916 0 : for (CpN = 1; CpN <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).TotalComponents; ++CpN) {
1917 0 : ShowContinueError(state,
1918 0 : format(" {}:{}",
1919 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).TypeOf,
1920 0 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LSN).Branch(BrN).Comp(CpN).Name));
1921 : }
1922 : }
1923 : }
1924 0 : }
1925 :
1926 175 : int MyPlantSizingIndex(EnergyPlusData &state,
1927 : std::string_view const CompType, // component description
1928 : std::string_view const CompName, // user name of component
1929 : int const NodeNumIn, // component water inlet node
1930 : [[maybe_unused]] int const NodeNumOut, // component water outlet node
1931 : bool &ErrorsFound, // set to true if there's an error, unchanged otherwise
1932 : bool const PrintErrorFlag // used for WSHP's where condenser loop may not be on a plant loop
1933 : )
1934 : {
1935 :
1936 : // FUNCTION INFORMATION:
1937 : // AUTHOR Fred Buhl
1938 : // DATE WRITTEN July 2008
1939 : // MODIFIED na
1940 : // RE-ENGINEERED na
1941 :
1942 : // PURPOSE OF THIS FUNCTION:
1943 : // Identify the correct Plant Sizing object for demand-side components such as heating and
1944 : // cooling coils.
1945 :
1946 : // METHODOLOGY EMPLOYED:
1947 : // This function searches all plant loops for a component whose input and
1948 : // output nodes match the desired input & output nodes. This plant loop index is then used
1949 : // to search the Plant Sizing array for the matching Plant Sizing object.
1950 :
1951 : // Using/Aliasing
1952 : using DataSizing::PlantSizingData;
1953 :
1954 : // Return value
1955 : int MyPltSizNum; // returned plant sizing index
1956 :
1957 175 : int MyPltLoopNum{};
1958 175 : PlantLocation DummyPlantLoc{};
1959 :
1960 175 : MyPltSizNum = 0;
1961 :
1962 175 : ScanPlantLoopsForNodeNum(state, "MyPlantSizingIndex", NodeNumIn, DummyPlantLoc);
1963 :
1964 175 : if (DummyPlantLoc.loopNum > 0) {
1965 175 : MyPltLoopNum = DummyPlantLoc.loopNum;
1966 : } else {
1967 0 : MyPltLoopNum = 0;
1968 : }
1969 :
1970 175 : if (MyPltLoopNum > 0) {
1971 175 : if (state.dataSize->NumPltSizInput > 0) {
1972 : MyPltSizNum =
1973 173 : Util::FindItemInList(state.dataPlnt->PlantLoop(MyPltLoopNum).Name, state.dataSize->PlantSizData, &PlantSizingData::PlantLoopName);
1974 : }
1975 175 : if (MyPltSizNum == 0) {
1976 4 : if (PrintErrorFlag) {
1977 4 : ShowSevereError(
1978 4 : state, format("MyPlantSizingIndex: Could not find {} in Sizing:Plant objects.", state.dataPlnt->PlantLoop(MyPltLoopNum).Name));
1979 2 : ShowContinueError(state, format("...reference Component Type=\"{}\", Name=\"{}\".", CompType, CompName));
1980 : }
1981 4 : ErrorsFound = true;
1982 : }
1983 : } else {
1984 0 : if (PrintErrorFlag) {
1985 0 : ShowWarningError(state, format("MyPlantSizingIndex: Could not find {} with name {} on any plant loop", CompType, CompName));
1986 : }
1987 0 : ErrorsFound = true;
1988 : }
1989 :
1990 175 : return MyPltSizNum;
1991 : }
1992 :
1993 5 : bool verifyTwoNodeNumsOnSamePlantLoop(EnergyPlusData &state, int const nodeIndexA, int const nodeIndexB)
1994 : {
1995 : // this function simply searches across plant loops looking for node numbers
1996 : // it returns true if the two nodes are found to be on the same loop
1997 : // it returns false otherwise
1998 : // because this is a nested loop, there's no reason it should be called except in one-time fashion
1999 5 : int matchedIndexA = 0;
2000 5 : int matchedIndexB = 0;
2001 12 : for (int loopNum = 1; loopNum <= state.dataPlnt->TotNumLoops; loopNum++) {
2002 21 : for (auto const &loopSide : state.dataPlnt->PlantLoop(loopNum).LoopSide) {
2003 28 : for (auto const &branch : loopSide.Branch) {
2004 28 : for (auto const &comp : branch.Comp) {
2005 14 : if (comp.NodeNumIn == nodeIndexA || comp.NodeNumOut == nodeIndexA) {
2006 5 : matchedIndexA = loopNum;
2007 : }
2008 14 : if (comp.NodeNumIn == nodeIndexB || comp.NodeNumOut == nodeIndexB) {
2009 5 : matchedIndexB = loopNum;
2010 : }
2011 : }
2012 : }
2013 : }
2014 : }
2015 5 : return (matchedIndexA == matchedIndexB) && (matchedIndexA != 0); // only return true if both are equal and non-zero
2016 : }
2017 :
2018 : Real64
2019 8 : MinFlowIfBranchHasVSPump(EnergyPlusData &state, PlantLocation const &plantLoc, bool &foundBranchPump, bool &foundLoopPump, bool const setFlowStatus)
2020 : {
2021 8 : Real64 branchPumpMinFlowLimit = 0.0;
2022 :
2023 8 : int NumCompsOnThisBranch = plantLoc.branch->TotalComponents;
2024 16 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
2025 8 : auto &component = plantLoc.branch->Comp(CompCounter);
2026 8 : if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
2027 8 : component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
2028 0 : foundBranchPump = true;
2029 0 : if (component.CompNum > 0) branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
2030 0 : break;
2031 : }
2032 : }
2033 :
2034 8 : if (!foundBranchPump) {
2035 : // second, if no branch pump, search for variable speed pump on inlet branch of supply side of this loop
2036 8 : if (plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches > 1) {
2037 0 : int NumCompsOnInletBranch = plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents;
2038 0 : for (int CompCounter = 1; CompCounter <= NumCompsOnInletBranch; ++CompCounter) {
2039 0 : auto &component = plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(CompCounter);
2040 0 : if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
2041 0 : component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
2042 0 : foundLoopPump = true;
2043 0 : if (component.CompNum > 0) branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
2044 0 : break;
2045 : }
2046 : }
2047 : }
2048 : }
2049 :
2050 8 : if (setFlowStatus) {
2051 0 : if (branchPumpMinFlowLimit > 0.0 && foundBranchPump) {
2052 0 : plantLoc.comp->FlowPriority = DataPlant::LoopFlowStatus::NeedyIfLoopOn;
2053 : } else {
2054 0 : plantLoc.comp->FlowPriority = DataPlant::LoopFlowStatus::TakesWhatGets;
2055 : }
2056 : }
2057 :
2058 8 : return branchPumpMinFlowLimit;
2059 : }
2060 :
2061 : } // namespace EnergyPlus::PlantUtilities
|