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 173380 : 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 173380 : tmpMinCompMdot = MinCompMdot;
102 173380 : tmpMaxCompMdot = MaxCompMdot;
103 : // trap bad values that can happen before all the setup is done
104 173380 : if (tmpMinCompMdot < 0.0) {
105 0 : tmpMinCompMdot = 0.0;
106 : }
107 173380 : if (tmpMaxCompMdot < 0.0) {
108 1002 : tmpMaxCompMdot = 0.0;
109 : }
110 :
111 : // reset outlet node
112 173380 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
113 :
114 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRateMin = tmpMinCompMdot;
115 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail = tmpMinCompMdot;
116 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRateMax = tmpMaxCompMdot;
117 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = tmpMaxCompMdot;
118 : // reset inlet node, but only change from inlet setting if set and more restrictive
119 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRate = 0.0;
120 173380 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = 0.0;
121 173380 : }
122 :
123 306685755 : 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 306685755 : if (plantLoc.loopNum == 0) { // protect from hard crash below
141 0 : if (InletNode > 0) {
142 0 : ShowSevereError(state,
143 0 : format("SetComponentFlowRate: trapped plant loop index = 0, check component with inlet node named={}",
144 0 : state.dataLoopNodes->NodeID(InletNode)));
145 : } else {
146 0 : ShowSevereError(state, "SetComponentFlowRate: trapped plant loop node id = 0");
147 : }
148 0 : 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 306685755 : Real64 const MdotOldRequest = state.dataLoopNodes->Node(InletNode).MassFlowRateRequest;
154 :
155 306685755 : if (plantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::Demand) {
156 : // store flow request on inlet node
157 204962422 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
158 204962422 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
159 204962422 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
160 204962422 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
161 204962422 : 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 101723333 : state.dataLoopNodes->Node(InletNode).MassFlowRateRequest = CompFlow;
166 : }
167 :
168 : // Update Min/Max Avail
169 :
170 306685755 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail =
171 306685755 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMin);
172 306685755 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax >= 0.0) {
173 306683776 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
174 306683776 : min(state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(InletNode).MassFlowRateMax);
175 : } else {
176 1979 : 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 0 : if (!state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued) {
179 0 : ShowSevereError(state,
180 0 : format("SetComponentFlowRate: check component model implementation for component with inlet node named={}",
181 0 : state.dataLoopNodes->NodeID(InletNode)));
182 0 : ShowContinueError(state, format("Inlet node MassFlowRatMax = {:.8R}", state.dataLoopNodes->Node(InletNode).MassFlowRateMax));
183 0 : state.dataLoopNodes->Node(InletNode).plantNodeErrorMsgIssued = true;
184 : }
185 : }
186 : }
187 :
188 : // Set loop flow rate
189 306685755 : if (plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked) {
190 213175868 : if (plantLoc.loop->MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
191 23026 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
192 23026 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
193 : } else { // bound the flow by Min/Max available and hardware limits
194 213152842 : if (plantLoc.comp->FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
195 : // determine highest flow request for all the components on the branch
196 3524019 : Real64 SeriesBranchHighFlowRequest = 0.0;
197 3524019 : Real64 SeriesBranchHardwareMaxLim = state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
198 3524019 : Real64 SeriesBranchHardwareMinLim = 0.0;
199 3524019 : Real64 SeriesBranchMaxAvail = state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail;
200 3524019 : 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 3524019 : bool EMSLoadOverride = false;
205 :
206 11044436 : for (int CompNum = 1; CompNum <= plantLoc.branch->TotalComponents; ++CompNum) {
207 7520417 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
208 7520417 : int const CompInletNodeNum = thisComp.NodeNumIn;
209 7520417 : auto const &thisInletNode = state.dataLoopNodes->Node(CompInletNodeNum);
210 7520417 : SeriesBranchHighFlowRequest = max(thisInletNode.MassFlowRateRequest, SeriesBranchHighFlowRequest);
211 7520417 : SeriesBranchHardwareMaxLim = min(thisInletNode.MassFlowRateMax, SeriesBranchHardwareMaxLim);
212 7520417 : SeriesBranchHardwareMinLim = max(thisInletNode.MassFlowRateMin, SeriesBranchHardwareMinLim);
213 7520417 : SeriesBranchMaxAvail = min(thisInletNode.MassFlowRateMaxAvail, SeriesBranchMaxAvail);
214 7520417 : 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 7520417 : if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) {
217 287874 : EMSLoadOverride = true;
218 : }
219 : }
220 :
221 3524019 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
222 143937 : SeriesBranchHardwareMaxLim = 0.0;
223 : }
224 :
225 : // take higher of branch max flow request and this new flow request
226 3524019 : CompFlow = max(CompFlow, SeriesBranchHighFlowRequest);
227 :
228 : // apply constraints on component flow
229 3524019 : CompFlow = max(CompFlow, SeriesBranchHardwareMinLim);
230 3524019 : CompFlow = max(CompFlow, SeriesBranchMinAvail);
231 3524019 : CompFlow = min(CompFlow, SeriesBranchHardwareMaxLim);
232 3524019 : CompFlow = min(CompFlow, SeriesBranchMaxAvail);
233 :
234 3524019 : if (CompFlow < DataBranchAirLoopPlant::MassFlowTolerance) {
235 1764055 : CompFlow = 0.0;
236 : }
237 3524019 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = CompFlow;
238 3524019 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
239 11044436 : for (int CompNum = 1; CompNum <= plantLoc.branch->TotalComponents; ++CompNum) {
240 7520417 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
241 7520417 : int const CompInletNodeNum = thisComp.NodeNumIn;
242 7520417 : int const CompOutletNodeNum = thisComp.NodeNumOut;
243 7520417 : state.dataLoopNodes->Node(CompInletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
244 7520417 : state.dataLoopNodes->Node(CompOutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
245 : }
246 :
247 : } else { // not series active
248 209628823 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = max(state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail, CompFlow);
249 209628823 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
250 209628823 : max(state.dataLoopNodes->Node(InletNode).MassFlowRateMin, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
251 209628823 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
252 209628823 : min(state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(OutletNode).MassFlowRate);
253 209628823 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
254 209628823 : 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 209628823 : bool EMSLoadOverride = false;
259 :
260 419457322 : 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 209828499 : auto const &thisComp = plantLoc.branch->Comp(CompNum);
263 209828499 : if (thisComp.EMSLoadOverrideOn && thisComp.EMSLoadOverrideValue == 0.0) {
264 0 : EMSLoadOverride = true;
265 : }
266 : }
267 :
268 209628823 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
269 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
270 : }
271 :
272 209628823 : if (state.dataLoopNodes->Node(OutletNode).MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) {
273 97710475 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
274 : }
275 209628823 : CompFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
276 209628823 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
277 : }
278 : }
279 93509887 : } else if (plantLoc.side->FlowLock == DataPlant::FlowLock::Locked) {
280 93509887 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
281 93509887 : 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 306685755 : if (plantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::Demand) {
287 204962422 : if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
288 99517565 : if (std::abs(MdotOldRequest - state.dataLoopNodes->Node(InletNode).MassFlowRateRequest) >
289 : DataBranchAirLoopPlant::MassFlowTolerance) { // demand comp changed its flow request
290 36869962 : plantLoc.side->SimLoopSideNeeded = true;
291 : }
292 : }
293 : }
294 : }
295 :
296 190770620 : 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 190770620 : auto &a_node = state.dataLoopNodes->Node(ActuatedNode);
320 190770620 : if (plantLoc.loopNum == 0 || plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
321 : // early in simulation before plant loops are setup and found
322 0 : a_node.MassFlowRate = CompFlow;
323 0 : return;
324 : }
325 :
326 190770620 : auto &loop_side = state.dataPlnt->PlantLoop(plantLoc.loopNum).LoopSide(plantLoc.loopSideNum);
327 :
328 : // store original flow
329 190770620 : Real64 const MdotOldRequest = a_node.MassFlowRateRequest;
330 190770620 : a_node.MassFlowRateRequest = CompFlow;
331 190770620 : if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid && (!ResetMode)) {
332 153161396 : if ((MdotOldRequest > 0.0) && (CompFlow > 0.0)) { // sure that not coming back from a no flow reset
333 128811015 : if ((std::abs(MdotOldRequest - a_node.MassFlowRateRequest) > DataBranchAirLoopPlant::MassFlowTolerance) &&
334 53588669 : (loop_side.FlowLock == DataPlant::FlowLock::Unlocked)) {
335 52700310 : loop_side.SimLoopSideNeeded = true;
336 : }
337 : }
338 : }
339 : // Set loop flow rate
340 :
341 190770620 : if (plantLoc.loopNum > 0 && plantLoc.loopSideNum != DataPlant::LoopSideLocation::Invalid) {
342 190770620 : auto const &branch = loop_side.Branch(plantLoc.branchNum);
343 190770620 : if (loop_side.FlowLock == DataPlant::FlowLock::Unlocked) {
344 187851759 : if (state.dataPlnt->PlantLoop(plantLoc.loopNum).MaxVolFlowRate == DataSizing::AutoSize) { // still haven't sized the plant loop
345 21568 : a_node.MassFlowRate = CompFlow;
346 : } else { // bound the flow by Min/Max available across entire branch
347 :
348 187830191 : a_node.MassFlowRate = max(a_node.MassFlowRateMinAvail, CompFlow);
349 187830191 : 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 187830191 : bool EMSLoadOverride = false;
355 : // check to see if any component on branch uses EMS On/Off Supervisory control to shut down flow
356 375660382 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
357 187830191 : auto const &comp = branch.Comp(CompNum);
358 187830191 : if (comp.EMSLoadOverrideOn && comp.EMSLoadOverrideValue == 0.0) {
359 0 : EMSLoadOverride = true;
360 : }
361 : }
362 187830191 : if (EMSLoadOverride) { // actuate EMS controlled components to 0 if On/Off Supervisory control is active off
363 0 : a_node.MassFlowRate = 0.0;
364 0 : a_node.MassFlowRateRequest = 0.0;
365 : }
366 :
367 187830191 : a_node.MassFlowRate = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRate);
368 187830191 : a_node.MassFlowRate = min(a_node.MassFlowRateMax, a_node.MassFlowRate);
369 187830191 : if (a_node.MassFlowRate < DataBranchAirLoopPlant::MassFlowTolerance) {
370 88325261 : a_node.MassFlowRate = 0.0;
371 : }
372 375660382 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
373 187830191 : auto const &comp = branch.Comp(CompNum);
374 187830191 : if (ActuatedNode == comp.NodeNumIn) {
375 : // ! found controller set to inlet of a component. now set that component's outlet
376 187830191 : int const NodeNum = comp.NodeNumOut;
377 187830191 : state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail = max(a_node.MassFlowRateMinAvail, a_node.MassFlowRateMin);
378 187830191 : state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = min(a_node.MassFlowRateMaxAvail, a_node.MassFlowRateMax);
379 187830191 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node.MassFlowRate;
380 : }
381 : }
382 : }
383 :
384 2918861 : } else if (loop_side.FlowLock == DataPlant::FlowLock::Locked) {
385 :
386 2918861 : CompFlow = a_node.MassFlowRate;
387 : // do not change requested flow rate either
388 2918861 : a_node.MassFlowRateRequest = MdotOldRequest;
389 2918861 : if ((CompFlow - a_node.MassFlowRateMaxAvail > DataBranchAirLoopPlant::MassFlowTolerance) ||
390 2918861 : (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 190770620 : Real64 const a_node_MasFlowRate(a_node.MassFlowRate);
404 190770620 : Real64 const a_node_MasFlowRateRequest(a_node.MassFlowRateRequest);
405 381541240 : for (int CompNum = 1, CompNum_end = branch.TotalComponents; CompNum <= CompNum_end; ++CompNum) {
406 190770620 : auto const &comp = branch.Comp(CompNum);
407 190770620 : int NodeNum = comp.NodeNumIn;
408 190770620 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
409 190770620 : state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
410 190770620 : NodeNum = comp.NodeNumOut;
411 190770620 : state.dataLoopNodes->Node(NodeNum).MassFlowRate = a_node_MasFlowRate;
412 190770620 : state.dataLoopNodes->Node(NodeNum).MassFlowRateRequest = a_node_MasFlowRateRequest;
413 : }
414 : }
415 : }
416 :
417 9563454 : 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 9563454 : Real64 constexpr ZeroLoad(0.0001);
449 :
450 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
451 : Real64 CompCurLoad;
452 : bool CompRunFlag;
453 :
454 9563454 : CompCurLoad = DataPlant::CompData::getPlantComponent(state, plantLoc).MyLoad;
455 9563454 : CompRunFlag = DataPlant::CompData::getPlantComponent(state, plantLoc).ON;
456 9563454 : DataPlant::OpScheme CompOpScheme = DataPlant::CompData::getPlantComponent(state, plantLoc).CurOpSchemeType;
457 :
458 9563454 : if (CompRunFlag) {
459 :
460 4748921 : switch (CompOpScheme) {
461 :
462 3830875 : case DataPlant::OpScheme::HeatingRB:
463 : case DataPlant::OpScheme::CoolingRB:
464 : case DataPlant::OpScheme::CompSetPtBased: { // These provide meaningful MyLoad values
465 3830875 : if (std::abs(CompCurLoad) > ZeroLoad) {
466 3798001 : FlowVal = TentativeFlowRequest;
467 : } else { // no load
468 32874 : FlowVal = 0.0;
469 : }
470 3830875 : break;
471 : }
472 918046 : default: { // Types that don't provide meaningful MyLoad values
473 918046 : FlowVal = TentativeFlowRequest;
474 918046 : break;
475 : }
476 : }
477 :
478 : } else { // runflag OFF
479 :
480 4814533 : FlowVal = 0.0;
481 : }
482 :
483 9563454 : return FlowVal;
484 : }
485 :
486 1454358 : 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 5207997 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
498 11270338 : for (DataPlant::LoopSideLocation LoopSide : DataPlant::LoopSideKeys) {
499 7516699 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.Exists) {
500 7493501 : int const SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.NodeNumIn;
501 : // loop across branch outlet nodes and check mass continuity
502 7493501 : int const NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.TotalOutletNodes;
503 7493501 : Real64 SumOutletFlow = 0.0;
504 41846569 : for (int OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
505 34353068 : int const BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Splitter.BranchNumOut(OutletNum);
506 34353068 : int const LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Branch(BranchNum).NodeNumOut;
507 34353068 : SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
508 : }
509 7493501 : Real64 const AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
510 7493501 : if (AbsDifference > DataPlant::CriteriaDelta_MassFlowRate) {
511 8319 : return true;
512 : }
513 : }
514 : }
515 : }
516 1446039 : return false;
517 : }
518 :
519 7537310 : 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 7537310 : if (!state.dataPlnt->PlantLoop(LoopNum).LoopHasConnectionComp) {
545 15074620 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag &&
546 15074620 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.Exists && !FirstHVACIteration) {
547 : // Find mixer outlet node number
548 1470413 : int MixerOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Mixer.NodeNumOut;
549 : // Find splitter inlet node number
550 1470413 : int SplitterInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.NodeNumIn;
551 :
552 1470413 : AbsDifference =
553 1470413 : std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - state.dataLoopNodes->Node(MixerOutletNode).MassFlowRate);
554 1470413 : 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 1470413 : int NumSplitterOutlets = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.TotalOutletNodes;
599 1470413 : SumOutletFlow = 0.0;
600 : // SumInletFlow = 0.0;
601 7669058 : for (int OutletNum = 1; OutletNum <= NumSplitterOutlets; ++OutletNum) {
602 6198645 : int BranchNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Splitter.BranchNumOut(OutletNum);
603 6198645 : int LastNodeOnBranch = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).Branch(BranchNum).NodeNumOut;
604 6198645 : SumOutletFlow += state.dataLoopNodes->Node(LastNodeOnBranch).MassFlowRate;
605 : // FirstNodeOnBranch= PlantLoop(LoopNum)%LoopSide(LoopSideNum)%Branch(BranchNum)%NodeNumIn
606 : // SumInletFlow = SumInletFlow + Node(FirstNodeOnBranch)%MassFlowRate
607 : }
608 1470413 : AbsDifference = std::abs(state.dataLoopNodes->Node(SplitterInletNode).MassFlowRate - SumOutletFlow);
609 1470413 : 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 7537310 : }
636 :
637 7537310 : 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 7537310 : Real64 constexpr OverShootOffset(5.0);
664 7537310 : Real64 constexpr UnderShootOffset(5.0);
665 7537310 : Real64 constexpr FatalOverShootOffset(200.0);
666 7537310 : 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 7537310 : 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 7537310 : makefatalerror = false;
684 7537310 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
685 7537310 : (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + OverShootOffset)) {
686 :
687 : // first stage, throw recurring warning that plant loop is getting out of control
688 67739 : ShowRecurringWarningErrorAtEnd(state,
689 19354 : "Plant loop exceeding upper temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name + "\"",
690 9677 : state.dataPlnt->PlantLoop(LoopNum).MaxTempErrIndex,
691 9677 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
692 :
693 9677 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp >
694 9677 : (state.dataPlnt->PlantLoop(LoopNum).MaxTemp + FatalOverShootOffset)) {
695 0 : hotcold = "hot";
696 0 : makefatalerror = true;
697 : }
698 : }
699 :
700 7537310 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
701 7537310 : (state.dataPlnt->PlantLoop(LoopNum).MinTemp - UnderShootOffset)) {
702 :
703 : // first stage, throw recurring warning that plant loop is getting out of control
704 9960 : ShowRecurringWarningErrorAtEnd(state,
705 2490 : "Plant loop falling below lower temperature limit, PlantLoop=\"" + state.dataPlnt->PlantLoop(LoopNum).Name +
706 : "\"",
707 1245 : state.dataPlnt->PlantLoop(LoopNum).MinTempErrIndex,
708 : _,
709 1245 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp);
710 :
711 1245 : if (state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).NodeNumOut).Temp <
712 1245 : (state.dataPlnt->PlantLoop(LoopNum).MinTemp - FatalUnderShootOffset)) {
713 0 : hotcold = "cold";
714 0 : makefatalerror = true;
715 : }
716 : }
717 :
718 7537310 : 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 7537310 : }
820 :
821 6015875 : 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 13945297 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
836 23788266 : for (DataPlant::LoopSideLocation LoopSideNum : DataPlant::LoopSideKeys) {
837 15858844 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSideNum).FlowLock = Value;
838 : }
839 : }
840 6015875 : }
841 :
842 6015413 : 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 13943678 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
855 23784795 : for (auto &e : state.dataPlnt->PlantLoop(LoopNum).LoopSide) {
856 15856530 : e.SimAirLoopsNeeded = false;
857 15856530 : e.SimZoneEquipNeeded = false;
858 15856530 : e.SimNonZoneEquipNeeded = false;
859 15856530 : e.SimElectLoadCentrNeeded = false;
860 : }
861 : }
862 6015413 : }
863 :
864 19149452 : 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 19149452 : 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 332 : int const CurrentNumChecksStored(static_cast<int>(state.dataPlantUtilities->CriteriaChecks.size() + 1));
899 332 : state.dataPlantUtilities->CriteriaChecks.redimension(CurrentNumChecksStored);
900 :
901 : // Store the unique name and location
902 332 : state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopNum = plantLoc.loopNum;
903 332 : state.dataPlantUtilities->CriteriaChecks(CurrentNumChecksStored).CallingCompLoopSideNum = plantLoc.loopSideNum;
904 :
905 332 : 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 332 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
911 :
912 : // Make sure we return the proper value of index
913 332 : 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 19149120 : CriteriaData CurCriteria = state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex);
922 :
923 : // Initialize, then check if we are out of range
924 19149120 : switch (CriteriaType) {
925 19149120 : case DataPlant::CriteriaType::MassFlowRate: {
926 19149120 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_MassFlowRate) {
927 175807 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
928 : }
929 19149120 : } break;
930 0 : case DataPlant::CriteriaType::Temperature: {
931 0 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_Temperature) {
932 0 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
933 : }
934 0 : } break;
935 0 : case DataPlant::CriteriaType::HeatTransferRate: {
936 0 : if (std::abs(CurCriteria.ThisCriteriaCheckValue - CriteriaValue) > DataPlant::CriteriaDelta_HeatTransferRate) {
937 0 : state.dataPlnt->PlantLoop(ConnectedPlantLoc.loopNum).LoopSide(ConnectedPlantLoc.loopSideNum).SimLoopSideNeeded = true;
938 : }
939 0 : } 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 19149452 : state.dataPlantUtilities->CriteriaChecks(UniqueCriteriaCheckIndex).ThisCriteriaCheckValue = CriteriaValue;
949 19149452 : }
950 :
951 11958926 : 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 11958926 : 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 11958926 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) {
988 42746 : DidAnythingChange = true;
989 : }
990 :
991 11958926 : if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) {
992 42746 : DidAnythingChange = true;
993 : }
994 :
995 11958926 : if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) {
996 4394356 : DidAnythingChange = true;
997 : }
998 :
999 11958926 : if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) {
1000 1726966 : DidAnythingChange = true;
1001 : }
1002 :
1003 : // could also check heat rate against McDeltaT from node data
1004 :
1005 11958926 : 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 6599 : DidAnythingChange = true;
1010 : }
1011 :
1012 11958926 : if (DidAnythingChange || FirstHVACIteration) {
1013 : // use current mass flow rate and inlet temp from Node and recalculate outlet temp
1014 8114554 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
1015 : // update node outlet conditions
1016 3902870 : Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, ModelInletTemp, RoutineName);
1017 3902870 : state.dataLoopNodes->Node(OutletNodeNum).Temp =
1018 3902870 : 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 8114554 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1027 :
1028 : // set sim flag on connected loops to true because this side changed
1029 8114554 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1030 22283385 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1031 : // see 10133
1032 14176928 : 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 8114554 : } else { // nothing changed so turn off sim flag
1043 3844372 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1044 : }
1045 11958926 : }
1046 :
1047 484696 : 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 484696 : 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 484696 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) {
1084 50030 : DidAnythingChange = true;
1085 : }
1086 :
1087 484696 : if (state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate != ModelMassFlowRate) {
1088 50031 : DidAnythingChange = true;
1089 : }
1090 :
1091 484696 : if (state.dataLoopNodes->Node(InletNodeNum).Temp != ModelInletTemp) {
1092 273605 : DidAnythingChange = true;
1093 : }
1094 :
1095 484696 : if (state.dataLoopNodes->Node(OutletNodeNum).Temp != ModelOutletTemp) {
1096 146186 : DidAnythingChange = true;
1097 : }
1098 :
1099 : // could also check heat rate against McDeltaT from node data
1100 :
1101 484696 : 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 10 : DidAnythingChange = true;
1105 : }
1106 :
1107 484696 : if (DidAnythingChange || FirstHVACIteration) {
1108 : // use current mass flow rate and inlet temp from Node and recalculate outlet temp
1109 382421 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
1110 : // update node outlet conditions
1111 230919 : Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, ModelInletTemp, RoutineName);
1112 230919 : state.dataLoopNodes->Node(OutletNodeNum).Temp =
1113 230919 : 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 382421 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1122 :
1123 : // set sim flag on connected loops to true because this side changed
1124 382421 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1125 785793 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1126 : // see 10133
1127 523862 : 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 261931 : int OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
1131 261931 : OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
1132 261931 : state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
1133 : }
1134 : }
1135 : }
1136 :
1137 382421 : } else { // nothing changed so turn off sim flag
1138 102275 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1139 : }
1140 484696 : }
1141 :
1142 22253 : 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 22253 : 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 22253 : if (state.dataLoopNodes->Node(InletNodeNum).MassFlowRate != ModelMassFlowRate) {
1174 4780 : DidAnythingChange = true;
1175 : }
1176 :
1177 22253 : 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 22253 : if (DidAnythingChange || FirstHVACIteration) {
1185 :
1186 : // set sim flag for this loop
1187 13521 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = true;
1188 :
1189 : // set sim flag on connected loops to true because this side changed
1190 13521 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected > 0) {
1191 40563 : for (int ConnectLoopNum = 1; ConnectLoopNum <= state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).TotalConnected; ++ConnectLoopNum) {
1192 27042 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopDemandsOnRemote) {
1193 13521 : int OtherLoopNum = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopNum;
1194 13521 : OtherLoopSide = state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).Connected(ConnectLoopNum).LoopSideNum;
1195 13521 : state.dataPlnt->PlantLoop(OtherLoopNum).LoopSide(OtherLoopSide).SimLoopSideNeeded = true;
1196 : }
1197 : }
1198 : }
1199 :
1200 13521 : } else { // nothing changed so turn off sim flag
1201 8732 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(LoopSide).SimLoopSideNeeded = false;
1202 : }
1203 22253 : }
1204 :
1205 459 : 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 459 : if (Loop1PlantLoc.loopNum == 0 || Loop1PlantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid || Loop2PlantLoc.loopNum == 0 ||
1225 459 : 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 459 : bool const Loop2DemandsOnLoop1(!Loop1DemandsOnLoop2);
1230 :
1231 : int TotalConnected;
1232 :
1233 459 : auto &loop_side_1 = state.dataPlnt->PlantLoop(Loop1PlantLoc.loopNum).LoopSide(Loop1PlantLoc.loopSideNum);
1234 459 : auto &connected_1 = loop_side_1.Connected;
1235 459 : if (allocated(connected_1)) {
1236 151 : TotalConnected = ++loop_side_1.TotalConnected;
1237 151 : connected_1.redimension(TotalConnected);
1238 : } else {
1239 308 : TotalConnected = loop_side_1.TotalConnected = 1;
1240 308 : connected_1.allocate(1);
1241 : }
1242 459 : connected_1(TotalConnected).LoopNum = Loop2PlantLoc.loopNum;
1243 459 : connected_1(TotalConnected).LoopSideNum = Loop2PlantLoc.loopSideNum;
1244 459 : connected_1(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
1245 459 : connected_1(TotalConnected).LoopDemandsOnRemote = Loop1DemandsOnLoop2;
1246 :
1247 459 : auto &loop_side_2 = state.dataPlnt->PlantLoop(Loop2PlantLoc.loopNum).LoopSide(Loop2PlantLoc.loopSideNum);
1248 459 : auto &connected_2 = loop_side_2.Connected;
1249 459 : if (allocated(connected_2)) {
1250 153 : TotalConnected = ++loop_side_2.TotalConnected;
1251 153 : connected_2.redimension(TotalConnected);
1252 : } else {
1253 306 : TotalConnected = loop_side_2.TotalConnected = 1;
1254 306 : connected_2.allocate(1);
1255 : }
1256 459 : connected_2(TotalConnected).LoopNum = Loop1PlantLoc.loopNum;
1257 459 : connected_2(TotalConnected).LoopSideNum = Loop1PlantLoc.loopSideNum;
1258 459 : connected_2(TotalConnected).ConnectorTypeOf_Num = static_cast<int>(ComponentType);
1259 459 : connected_2(TotalConnected).LoopDemandsOnRemote = Loop2DemandsOnLoop1;
1260 : }
1261 :
1262 78 : 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 78 : PlantCallingOrderInfoStruct RecordToMoveInPlantCallingOrderInfo;
1279 :
1280 78 : if (OldIndex == 0) {
1281 0 : ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, Old Index=0");
1282 : }
1283 78 : if (NewIndex == 0) {
1284 0 : ShowSevereError(state, "ShiftPlantLoopSideCallingOrder: developer error notice of invalid index, New Index=1");
1285 : }
1286 78 : if ((OldIndex == 0) || (NewIndex == 0)) {
1287 0 : return;
1288 : }
1289 :
1290 : // store copy of prior structure
1291 78 : Array1D<PlantCallingOrderInfoStruct> TempPlantCallingOrderInfo(state.dataPlnt->PlantCallingOrderInfo);
1292 :
1293 78 : RecordToMoveInPlantCallingOrderInfo = state.dataPlnt->PlantCallingOrderInfo(OldIndex);
1294 :
1295 78 : if (OldIndex == NewIndex) {
1296 : // do nothing, no shift needed.
1297 75 : } 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 1 : state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({2, NewIndex});
1302 1 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1303 2 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1304 3 : TempPlantCallingOrderInfo({NewIndex + 1, state.dataPlnt->TotNumHalfLoops});
1305 :
1306 74 : } 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 74 : } 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 74 : } 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 74 : } 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 6 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1330 6 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({1, OldIndex - 1});
1331 12 : state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1332 18 : TempPlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops});
1333 :
1334 68 : } 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 68 : state.dataPlnt->PlantCallingOrderInfo({1, NewIndex - 1}) = TempPlantCallingOrderInfo({1, NewIndex - 1});
1338 68 : state.dataPlnt->PlantCallingOrderInfo(NewIndex) = RecordToMoveInPlantCallingOrderInfo;
1339 68 : state.dataPlnt->PlantCallingOrderInfo({NewIndex + 1, OldIndex}) = TempPlantCallingOrderInfo({NewIndex, NewIndex + (OldIndex - NewIndex) - 1});
1340 136 : state.dataPlnt->PlantCallingOrderInfo({OldIndex + 1, state.dataPlnt->TotNumHalfLoops}) =
1341 204 : 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 78 : }
1349 :
1350 15201 : 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 15201 : NumPlantComps = state.dataSize->SaveNumPlantComps;
1383 :
1384 15201 : if (NumPlantComps == 0) { // first time in, fill and return
1385 464 : NumPlantComps = 1;
1386 464 : state.dataSize->CompDesWaterFlow.allocate(NumPlantComps);
1387 : // save the new data
1388 464 : state.dataSize->CompDesWaterFlow(NumPlantComps).SupNode = ComponentInletNodeNum;
1389 464 : state.dataSize->CompDesWaterFlow(NumPlantComps).DesVolFlowRate = DesPlantFlow;
1390 464 : state.dataSize->SaveNumPlantComps = NumPlantComps;
1391 464 : return;
1392 : }
1393 :
1394 14737 : Found = false;
1395 : // find node num index in structure if any
1396 437417 : for (PlantCompNum = 1; PlantCompNum <= NumPlantComps; ++PlantCompNum) {
1397 430680 : if (ComponentInletNodeNum == state.dataSize->CompDesWaterFlow(PlantCompNum).SupNode) {
1398 8000 : Found = true;
1399 8000 : thisCallNodeIndex = PlantCompNum;
1400 : }
1401 430680 : if (Found) {
1402 8000 : break;
1403 : }
1404 : }
1405 :
1406 14737 : if (!Found) { // grow structure and add new node at the end
1407 6737 : ++NumPlantComps; // increment the number of components that use water as a source of heat or coolth
1408 6737 : state.dataSize->CompDesWaterFlow.emplace_back(ComponentInletNodeNum, DesPlantFlow); // Append the new element
1409 6737 : state.dataSize->SaveNumPlantComps = NumPlantComps;
1410 : } else {
1411 8000 : state.dataSize->CompDesWaterFlow(thisCallNodeIndex).SupNode = ComponentInletNodeNum;
1412 8000 : state.dataSize->CompDesWaterFlow(thisCallNodeIndex).DesVolFlowRate = DesPlantFlow;
1413 : }
1414 : }
1415 :
1416 332037258 : 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 332037258 : state.dataLoopNodes->Node(OutletNodeNum).FluidType = state.dataLoopNodes->Node(InletNodeNum).FluidType;
1440 :
1441 332037258 : state.dataLoopNodes->Node(OutletNodeNum).Temp = state.dataLoopNodes->Node(InletNodeNum).Temp;
1442 332037258 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
1443 332037258 : state.dataLoopNodes->Node(OutletNodeNum).Quality = state.dataLoopNodes->Node(InletNodeNum).Quality;
1444 332037258 : state.dataLoopNodes->Node(OutletNodeNum).Enthalpy =
1445 332037258 : state.dataLoopNodes->Node(InletNodeNum).Enthalpy; // should have routines that keep this current with temp?
1446 :
1447 332037258 : state.dataLoopNodes->Node(OutletNodeNum).TempMin = state.dataLoopNodes->Node(InletNodeNum).TempMin;
1448 332037258 : state.dataLoopNodes->Node(OutletNodeNum).TempMax = state.dataLoopNodes->Node(InletNodeNum).TempMax;
1449 332037258 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMinAvail =
1450 332037258 : max(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMin, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMinAvail);
1451 332037258 : state.dataLoopNodes->Node(OutletNodeNum).MassFlowRateMaxAvail =
1452 332037258 : min(state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMax, state.dataLoopNodes->Node(InletNodeNum).MassFlowRateMaxAvail);
1453 :
1454 332037258 : 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 332037258 : if (present(LoopNum)) {
1458 220603743 : switch (state.dataPlnt->PlantLoop(LoopNum).PressureSimType) {
1459 220528813 : case DataPlant::PressSimType::NoPressure:
1460 220528813 : state.dataLoopNodes->Node(OutletNodeNum).Press = state.dataLoopNodes->Node(InletNodeNum).Press;
1461 220603743 : default:
1462 : // Don't do anything
1463 220603743 : break;
1464 : }
1465 : }
1466 332037258 : }
1467 :
1468 228299860 : 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 228299860 : BoundedValue = ValueToBound;
1487 228299860 : BoundedValue = max(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMinAvail);
1488 228299860 : BoundedValue = min(BoundedValue, state.dataLoopNodes->Node(NodeNumToBoundWith).MassFlowRateMaxAvail);
1489 :
1490 228299860 : return BoundedValue;
1491 : }
1492 :
1493 18660463 : 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 18660463 : OldMinAvail = state.dataLoopNodes->Node(NodeNum).MassFlowRateMinAvail;
1515 18660463 : 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 18660463 : if ((NewMinAvail > OldMinAvail) && (NewMinAvail <= OldMaxAvail)) {
1519 118541 : 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 18660463 : if ((NewMaxAvail < OldMaxAvail) && (NewMaxAvail >= OldMinAvail)) {
1524 170886 : state.dataLoopNodes->Node(NodeNum).MassFlowRateMaxAvail = NewMaxAvail;
1525 : }
1526 18660463 : }
1527 :
1528 18660463 : 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 18660463 : BoundedValue = ValueToBound;
1547 18660463 : BoundedValue = max(BoundedValue, LowerBound);
1548 18660463 : BoundedValue = min(BoundedValue, UpperBound);
1549 :
1550 18660463 : return BoundedValue;
1551 : }
1552 :
1553 0 : 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 0 : return (ValueToCheck >= LowerBound) && (ValueToCheck <= UpperBound);
1571 : }
1572 :
1573 : // In-Place Right Shift by 1 of Array Elements
1574 62309600 : void rshift1(Array1D<Real64> &a, Real64 const a_l)
1575 : {
1576 62309600 : assert(a.size_bounded());
1577 311548000 : for (int i = a.u(), e = a.l(); i > e; --i) {
1578 249238400 : a(i) = a(i - 1);
1579 : }
1580 62309600 : a(a.l()) = a_l;
1581 62309600 : }
1582 :
1583 2980977 : 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 10769677 : for (int ThisLoopNum = 1; ThisLoopNum <= isize(state.dataPlnt->PlantLoop); ++ThisLoopNum) {
1602 7788700 : auto &loop = state.dataPlnt->PlantLoop(ThisLoopNum);
1603 23366100 : for (DataPlant::LoopSideLocation ThisLoopSide : DataPlant::LoopSideKeys) {
1604 15577400 : auto &loop_side = loop.LoopSide(ThisLoopSide);
1605 :
1606 15577400 : if (FirstHVACIteration) {
1607 7537310 : loop_side.InletNode.TemperatureHistory = 0.0;
1608 7537310 : loop_side.InletNode.MassFlowRateHistory = 0.0;
1609 7537310 : loop_side.OutletNode.TemperatureHistory = 0.0;
1610 7537310 : loop_side.OutletNode.MassFlowRateHistory = 0.0;
1611 : }
1612 :
1613 15577400 : int InletNodeNum = loop_side.NodeNumIn;
1614 15577400 : Real64 InletNodeTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
1615 15577400 : Real64 InletNodeMdot = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
1616 :
1617 15577400 : int OutletNodeNum = loop_side.NodeNumOut;
1618 15577400 : Real64 OutletNodeTemp = state.dataLoopNodes->Node(OutletNodeNum).Temp;
1619 15577400 : Real64 OutletNodeMdot = state.dataLoopNodes->Node(OutletNodeNum).MassFlowRate;
1620 :
1621 15577400 : rshift1(loop_side.InletNode.TemperatureHistory, InletNodeTemp);
1622 15577400 : rshift1(loop_side.InletNode.MassFlowRateHistory, InletNodeMdot);
1623 15577400 : rshift1(loop_side.OutletNode.TemperatureHistory, OutletNodeTemp);
1624 15577400 : rshift1(loop_side.OutletNode.MassFlowRateHistory, OutletNodeMdot);
1625 : }
1626 : }
1627 2980977 : }
1628 :
1629 18177 : 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 18177 : FoundCount = 0;
1670 :
1671 18177 : FoundComponent = false;
1672 18177 : FoundCompName = false;
1673 18177 : StartingLoopNum = 1;
1674 18177 : EndingLoopNum = state.dataPlnt->TotNumLoops;
1675 18177 : if (present(SingleLoopSearch)) {
1676 1648 : StartingLoopNum = SingleLoopSearch;
1677 1648 : EndingLoopNum = SingleLoopSearch;
1678 : }
1679 :
1680 72410 : for (LoopCtr = StartingLoopNum; LoopCtr <= EndingLoopNum; ++LoopCtr) {
1681 54233 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1682 162699 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1683 108466 : auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
1684 1318937 : for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
1685 1210471 : auto &this_branch = this_loop_side.Branch(BranchCtr);
1686 2426550 : for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
1687 1216079 : auto &this_component = this_branch.Comp(CompCtr);
1688 1216079 : if (this_component.Type == CompType) {
1689 390455 : if (Util::SameString(CompName, this_component.Name)) {
1690 19311 : FoundCompName = true;
1691 19311 : if (present(InletNodeNumber)) {
1692 2426 : if (InletNodeNumber > 0) {
1693 : // check if inlet nodes agree
1694 2426 : if (InletNodeNumber == this_component.NodeNumIn) {
1695 1294 : FoundComponent = true;
1696 1294 : ++FoundCount;
1697 1294 : plantLoc.loopNum = LoopCtr;
1698 1294 : plantLoc.loopSideNum = LoopSideCtr;
1699 1294 : plantLoc.branchNum = BranchCtr;
1700 1294 : plantLoc.compNum = CompCtr;
1701 :
1702 1294 : plantLoc.loop = &this_loop;
1703 1294 : plantLoc.side = &this_loop_side;
1704 1294 : plantLoc.branch = &this_branch;
1705 1294 : plantLoc.comp = &this_component;
1706 : }
1707 : }
1708 : } else {
1709 16885 : FoundComponent = true;
1710 16885 : ++FoundCount;
1711 16885 : plantLoc.loopNum = LoopCtr;
1712 16885 : plantLoc.loopSideNum = LoopSideCtr;
1713 16885 : plantLoc.branchNum = BranchCtr;
1714 16885 : plantLoc.compNum = CompCtr;
1715 :
1716 16885 : plantLoc.loop = &this_loop;
1717 16885 : plantLoc.side = &this_loop_side;
1718 16885 : plantLoc.branch = &this_branch;
1719 16885 : plantLoc.comp = &this_component;
1720 : }
1721 19311 : if (present(LowLimitTemp)) {
1722 608 : this_component.MinOutletTemp = LowLimitTemp;
1723 : }
1724 19311 : if (present(HighLimitTemp)) {
1725 211 : this_component.MaxOutletTemp = HighLimitTemp;
1726 : }
1727 : }
1728 : }
1729 : }
1730 : }
1731 : }
1732 : }
1733 :
1734 18177 : bool skipErrors = false;
1735 18177 : if (present(suppressErrors)) {
1736 0 : skipErrors = suppressErrors;
1737 : }
1738 :
1739 18177 : if (!FoundComponent && !skipErrors) {
1740 0 : if (CompType != DataPlant::PlantEquipmentType::Invalid && CompType != DataPlant::PlantEquipmentType::Num) {
1741 0 : if (!present(SingleLoopSearch)) {
1742 0 : ShowSevereError(state,
1743 0 : format("Plant Component {} called \"{}\" was not found on any plant loops.",
1744 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(CompType)],
1745 0 : std::string{CompName}));
1746 0 : 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 0 : if (present(InletNodeNumber)) {
1755 0 : if (FoundCompName) {
1756 0 : ShowContinueError(state, format("Looking for matching inlet Node=\"{}\".", state.dataLoopNodes->NodeID(InletNodeNumber)));
1757 : }
1758 : }
1759 0 : 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 0 : 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 18177 : if (present(CountMatchPlantLoops)) {
1773 7257 : CountMatchPlantLoops = FoundCount;
1774 : }
1775 18177 : }
1776 :
1777 10446 : 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 10446 : inFoundCount = 0;
1805 10446 : outFoundCount = 0;
1806 10446 : if (present(CompNum)) {
1807 2 : CompNum = 0;
1808 : }
1809 10446 : FoundNode = false;
1810 :
1811 47174 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1812 36728 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1813 110184 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1814 73456 : auto &this_loop_side = this_loop.LoopSide(LoopSideCtr);
1815 1283950 : for (BranchCtr = 1; BranchCtr <= this_loop_side.TotalBranches; ++BranchCtr) {
1816 1210494 : auto &this_branch = this_loop_side.Branch(BranchCtr);
1817 2425924 : for (CompCtr = 1; CompCtr <= this_branch.TotalComponents; ++CompCtr) {
1818 1215430 : auto &this_comp = this_branch.Comp(CompCtr);
1819 1215430 : if (NodeNum == this_comp.NodeNumIn) {
1820 10446 : FoundNode = true;
1821 10446 : ++inFoundCount;
1822 10446 : plantLoc.loopNum = LoopCtr;
1823 10446 : plantLoc.loopSideNum = LoopSideCtr;
1824 10446 : plantLoc.branchNum = BranchCtr;
1825 :
1826 10446 : if (present(CompNum)) {
1827 2 : CompNum = CompCtr; // What is this? This is an input
1828 : }
1829 10446 : plantLoc.loop = &this_loop;
1830 10446 : plantLoc.side = &this_loop_side;
1831 10446 : plantLoc.branch = &this_branch;
1832 10446 : plantLoc.comp = &this_comp;
1833 : }
1834 :
1835 1215430 : if (NodeNum == this_comp.NodeNumOut) {
1836 2 : ++outFoundCount;
1837 2 : plantLoc.loopNum = LoopCtr;
1838 2 : plantLoc.loopSideNum = LoopSideCtr;
1839 2 : plantLoc.branchNum = BranchCtr;
1840 :
1841 2 : plantLoc.loop = &this_loop;
1842 2 : plantLoc.side = &this_loop_side;
1843 2 : plantLoc.branch = &this_branch;
1844 2 : plantLoc.comp = &this_comp;
1845 : }
1846 : }
1847 : }
1848 : }
1849 : }
1850 :
1851 10446 : 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 10446 : }
1866 :
1867 : // Utility function, mostly for unit tests.
1868 14042 : void SetPlantLocationLinks(EnergyPlusData &state, PlantLocation &plantLoc)
1869 : {
1870 14042 : if (plantLoc.loopNum == 0) {
1871 0 : return;
1872 : }
1873 14042 : plantLoc.loop = &state.dataPlnt->PlantLoop(plantLoc.loopNum);
1874 14042 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Invalid) {
1875 0 : return;
1876 : }
1877 14042 : plantLoc.side = &plantLoc.loop->LoopSide(plantLoc.loopSideNum);
1878 14042 : if (plantLoc.branchNum == 0) {
1879 0 : return;
1880 : }
1881 14042 : plantLoc.branch = &plantLoc.side->Branch(plantLoc.branchNum);
1882 14042 : if (plantLoc.compNum == 0) {
1883 0 : return;
1884 : }
1885 14042 : plantLoc.comp = &plantLoc.branch->Comp(plantLoc.compNum);
1886 : }
1887 :
1888 284707 : 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 284707 : AnyPlantLoopSidesNeedSim = false;
1907 :
1908 : // Then check if there are any
1909 494043 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1910 678895 : for (DataPlant::LoopSideLocation LoopSideCtr : DataPlant::LoopSideKeys) {
1911 469559 : if (state.dataPlnt->PlantLoop(LoopCtr).LoopSide(LoopSideCtr).SimLoopSideNeeded) {
1912 50887 : AnyPlantLoopSidesNeedSim = true;
1913 50887 : return AnyPlantLoopSidesNeedSim;
1914 : }
1915 : }
1916 : }
1917 :
1918 233820 : return AnyPlantLoopSidesNeedSim;
1919 : }
1920 :
1921 6753598 : 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 16215431 : for (LoopCtr = 1; LoopCtr <= state.dataPlnt->TotNumLoops; ++LoopCtr) {
1937 9461833 : auto &this_loop = state.dataPlnt->PlantLoop(LoopCtr);
1938 9461833 : this_loop.LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = Value;
1939 9461833 : this_loop.LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = Value;
1940 : }
1941 6753598 : }
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 9542 : 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 9542 : int MyPltLoopNum{};
2007 9542 : PlantLocation DummyPlantLoc{};
2008 :
2009 9542 : MyPltSizNum = 0;
2010 :
2011 9542 : ScanPlantLoopsForNodeNum(state, "MyPlantSizingIndex", NodeNumIn, DummyPlantLoc);
2012 :
2013 9542 : if (DummyPlantLoc.loopNum > 0) {
2014 9542 : MyPltLoopNum = DummyPlantLoc.loopNum;
2015 : } else {
2016 0 : MyPltLoopNum = 0;
2017 : }
2018 :
2019 9542 : if (MyPltLoopNum > 0) {
2020 9542 : if (state.dataSize->NumPltSizInput > 0) {
2021 : MyPltSizNum =
2022 9542 : Util::FindItemInList(state.dataPlnt->PlantLoop(MyPltLoopNum).Name, state.dataSize->PlantSizData, &PlantSizingData::PlantLoopName);
2023 : }
2024 9542 : if (MyPltSizNum == 0) {
2025 15 : if (PrintErrorFlag) {
2026 30 : ShowSevereError(
2027 30 : state, format("MyPlantSizingIndex: Could not find {} in Sizing:Plant objects.", state.dataPlnt->PlantLoop(MyPltLoopNum).Name));
2028 15 : ShowContinueError(state, format("...reference Component Type=\"{}\", Name=\"{}\".", CompType, CompName));
2029 : }
2030 15 : 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 9542 : return MyPltSizNum;
2040 : }
2041 :
2042 4 : 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 4 : int matchedIndexA = 0;
2049 4 : int matchedIndexB = 0;
2050 14 : for (int loopNum = 1; loopNum <= state.dataPlnt->TotNumLoops; loopNum++) {
2051 30 : for (auto const &loopSide : state.dataPlnt->PlantLoop(loopNum).LoopSide) {
2052 144 : for (auto const &branch : loopSide.Branch) {
2053 248 : for (auto const &comp : branch.Comp) {
2054 124 : if (comp.NodeNumIn == nodeIndexA || comp.NodeNumOut == nodeIndexA) {
2055 4 : matchedIndexA = loopNum;
2056 : }
2057 124 : if (comp.NodeNumIn == nodeIndexB || comp.NodeNumOut == nodeIndexB) {
2058 4 : matchedIndexB = loopNum;
2059 : }
2060 : }
2061 : }
2062 : }
2063 : }
2064 4 : return (matchedIndexA == matchedIndexB) && (matchedIndexA != 0); // only return true if both are equal and non-zero
2065 : }
2066 :
2067 : Real64
2068 1420387 : MinFlowIfBranchHasVSPump(EnergyPlusData &state, PlantLocation const &plantLoc, bool &foundBranchPump, bool &foundLoopPump, bool const setFlowStatus)
2069 : {
2070 1420387 : Real64 branchPumpMinFlowLimit = 0.0;
2071 :
2072 1420387 : int NumCompsOnThisBranch = plantLoc.branch->TotalComponents;
2073 2840778 : for (int CompCounter = 1; CompCounter <= NumCompsOnThisBranch; ++CompCounter) {
2074 1420393 : auto &component = plantLoc.branch->Comp(CompCounter);
2075 1420393 : if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
2076 1420391 : component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
2077 2 : foundBranchPump = true;
2078 2 : if (component.CompNum > 0) {
2079 2 : branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
2080 : }
2081 2 : break;
2082 : }
2083 : }
2084 :
2085 1420387 : if (!foundBranchPump) {
2086 : // second, if no branch pump, search for variable speed pump on inlet branch of supply side of this loop
2087 1420385 : if (plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches > 1) {
2088 1420385 : int NumCompsOnInletBranch = plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents;
2089 1996360 : for (int CompCounter = 1; CompCounter <= NumCompsOnInletBranch; ++CompCounter) {
2090 1420389 : auto &component = plantLoc.loop->LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(CompCounter);
2091 1420389 : if (component.Type == DataPlant::PlantEquipmentType::PumpVariableSpeed ||
2092 711154 : component.Type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) {
2093 844414 : foundLoopPump = true;
2094 844414 : if (component.CompNum > 0) {
2095 843952 : branchPumpMinFlowLimit = state.dataPumps->PumpEquip(component.CompNum).MassFlowRateMin;
2096 : }
2097 844414 : break;
2098 : }
2099 : }
2100 : }
2101 : }
2102 :
2103 1420387 : if (setFlowStatus) {
2104 14 : if (branchPumpMinFlowLimit > 0.0 && foundBranchPump) {
2105 0 : plantLoc.comp->FlowPriority = DataPlant::LoopFlowStatus::NeedyIfLoopOn;
2106 : } else {
2107 14 : plantLoc.comp->FlowPriority = DataPlant::LoopFlowStatus::TakesWhatGets;
2108 : }
2109 : }
2110 :
2111 1420387 : return branchPumpMinFlowLimit;
2112 : }
2113 :
2114 : } // namespace EnergyPlus::PlantUtilities
|