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