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