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