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/Fmath.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/Data/EnergyPlusData.hh>
56 : #include <EnergyPlus/DataAirLoop.hh>
57 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
58 : #include <EnergyPlus/DataContaminantBalance.hh>
59 : #include <EnergyPlus/DataConvergParams.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/FluidProperties.hh>
62 : #include <EnergyPlus/HVACInterfaceManager.hh>
63 : #include <EnergyPlus/OutputProcessor.hh>
64 : #include <EnergyPlus/Plant/DataPlant.hh>
65 : #include <EnergyPlus/PlantUtilities.hh>
66 : #include <EnergyPlus/UtilityRoutines.hh>
67 :
68 : namespace EnergyPlus::HVACInterfaceManager {
69 :
70 : // MODULE INFORMATION:
71 : // AUTHOR Rick Strand
72 : // DATE WRITTEN October 1998
73 : // MODIFIED na
74 : // RE-ENGINEERED na
75 :
76 : // PURPOSE OF THIS MODULE:
77 : // This module contains one or more routines for checking the convergence
78 : // of the various HVAC loops and passing information across interface
79 : // boundaries.
80 :
81 : // METHODOLOGY EMPLOYED:
82 : // The upper level HVAC managers call the routine(s) contained in this
83 : // module as a last step. The node information is passed across the
84 : // interface boundary and the logical flag is set if the nodes across
85 : // from each other are not within tolerance.
86 :
87 23146589 : void UpdateHVACInterface(EnergyPlusData &state,
88 : int const AirLoopNum, // airloop number for which air loop this is
89 : DataConvergParams::CalledFrom const CalledFrom,
90 : int const OutletNode, // Node number for the outlet of the side of the loop just simulated
91 : int const InletNode, // Node number for the inlet of the side that needs the outlet node data
92 : bool &OutOfToleranceFlag // True when the other side of the loop need to be (re)simulated
93 : )
94 : {
95 :
96 : // SUBROUTINE INFORMATION:
97 : // AUTHOR Rick Strand
98 : // DATE WRITTEN October 1998
99 :
100 : // PURPOSE OF THIS SUBROUTINE:
101 : // This subroutine manages any generic HVAC loop interface.
102 :
103 : // METHODOLOGY EMPLOYED:
104 : // This is a simple "forward" interface where all of the properties
105 : // from the outlet of one side of the loop get transferred directly
106 : // to the inlet node of the corresponding other side of the loop.
107 :
108 23146589 : auto &TmpRealARR = state.dataHVACInterfaceMgr->TmpRealARR;
109 23146589 : auto &airLoopConv = state.dataConvergeParams->AirLoopConvergence(AirLoopNum);
110 23146589 : auto &thisInletNode = state.dataLoopNodes->Node(InletNode);
111 :
112 23146589 : if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode == 0)) {
113 : // Air loop has no return path - only check mass flow and then set return inlet node mass flow to sum of demand side inlet nodes
114 :
115 15046 : airLoopConv.HVACMassFlowNotConverged[0] = false;
116 15046 : airLoopConv.HVACHumRatNotConverged[0] = false;
117 15046 : airLoopConv.HVACTempNotConverged[0] = false;
118 15046 : airLoopConv.HVACEnergyNotConverged[0] = false;
119 :
120 15046 : Real64 totDemandSideMassFlow = 0.0;
121 15046 : Real64 totDemandSideMinAvail = 0.0;
122 15046 : Real64 totDemandSideMaxAvail = 0.0;
123 30092 : for (int demIn = 1; demIn <= state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).NumSupplyNodes; ++demIn) {
124 15046 : int demInNode = state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).ZoneEquipSupplyNodeNum(demIn);
125 15046 : totDemandSideMassFlow += state.dataLoopNodes->Node(demInNode).MassFlowRate;
126 15046 : totDemandSideMinAvail += state.dataLoopNodes->Node(demInNode).MassFlowRateMinAvail;
127 15046 : totDemandSideMaxAvail += state.dataLoopNodes->Node(demInNode).MassFlowRateMaxAvail;
128 : }
129 15046 : TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
130 15046 : airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(totDemandSideMassFlow - thisInletNode.MassFlowRate);
131 150460 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
132 135414 : airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
133 : }
134 15046 : if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
135 5240 : airLoopConv.HVACMassFlowNotConverged[0] = true;
136 5240 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
137 : }
138 :
139 15046 : thisInletNode.MassFlowRate = totDemandSideMassFlow;
140 15046 : thisInletNode.MassFlowRateMinAvail = totDemandSideMinAvail;
141 15046 : thisInletNode.MassFlowRateMaxAvail = totDemandSideMaxAvail;
142 15046 : return;
143 : }
144 :
145 : // Calculate the approximate energy difference across interface for comparison
146 : Real64 DeltaEnergy =
147 46263086 : DataConvergParams::HVACCpApprox * ((state.dataLoopNodes->Node(OutletNode).MassFlowRate * state.dataLoopNodes->Node(OutletNode).Temp) -
148 46263086 : (thisInletNode.MassFlowRate * thisInletNode.Temp));
149 :
150 23131543 : if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode > 0)) {
151 :
152 10975788 : airLoopConv.HVACMassFlowNotConverged[0] = false;
153 10975788 : airLoopConv.HVACHumRatNotConverged[0] = false;
154 10975788 : airLoopConv.HVACTempNotConverged[0] = false;
155 10975788 : airLoopConv.HVACEnergyNotConverged[0] = false;
156 :
157 10975788 : TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
158 10975788 : airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
159 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
160 98782092 : airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
161 : }
162 10975788 : if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
163 2901659 : airLoopConv.HVACMassFlowNotConverged[0] = true;
164 2901659 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
165 : }
166 :
167 10975788 : TmpRealARR = airLoopConv.HVACHumDemandToSupplyTolValue;
168 10975788 : airLoopConv.HVACHumDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
169 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
170 98782092 : airLoopConv.HVACHumDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
171 : }
172 10975788 : if (airLoopConv.HVACHumDemandToSupplyTolValue[0] > DataConvergParams::HVACHumRatToler) {
173 416757 : airLoopConv.HVACHumRatNotConverged[0] = true;
174 416757 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
175 : }
176 :
177 10975788 : TmpRealARR = airLoopConv.HVACTempDemandToSupplyTolValue;
178 10975788 : airLoopConv.HVACTempDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
179 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
180 98782092 : airLoopConv.HVACTempDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
181 : }
182 10975788 : if (airLoopConv.HVACTempDemandToSupplyTolValue[0] > DataConvergParams::HVACTemperatureToler) {
183 2439037 : airLoopConv.HVACTempNotConverged[0] = true;
184 2439037 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
185 : }
186 :
187 10975788 : TmpRealARR = airLoopConv.HVACEnergyDemandToSupplyTolValue;
188 10975788 : airLoopConv.HVACEnergyDemandToSupplyTolValue[0] = std::abs(DeltaEnergy);
189 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
190 98782092 : airLoopConv.HVACEnergyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
191 : }
192 10975788 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
193 4038732 : airLoopConv.HVACEnergyNotConverged[0] = true;
194 4038732 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
195 : }
196 :
197 10975788 : TmpRealARR = airLoopConv.HVACEnthalpyDemandToSupplyTolValue;
198 10975788 : airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
199 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
200 98782092 : airLoopConv.HVACEnthalpyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
201 : }
202 10975788 : if (airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
203 1110714 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
204 : }
205 :
206 10975788 : TmpRealARR = airLoopConv.HVACPressureDemandToSupplyTolValue;
207 10975788 : airLoopConv.HVACPressureDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
208 109757880 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
209 98782092 : airLoopConv.HVACPressureDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
210 : }
211 10975788 : if (airLoopConv.HVACPressureDemandToSupplyTolValue[0] > DataConvergParams::HVACPressToler) {
212 11798 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
213 10975788 : }
214 :
215 12155755 : } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck1) {
216 :
217 12100880 : airLoopConv.HVACMassFlowNotConverged[1] = false;
218 12100880 : airLoopConv.HVACHumRatNotConverged[1] = false;
219 12100880 : airLoopConv.HVACTempNotConverged[1] = false;
220 12100880 : airLoopConv.HVACEnergyNotConverged[1] = false;
221 :
222 12100880 : TmpRealARR = airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue;
223 12100880 : airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] =
224 12100880 : std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
225 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
226 108907920 : airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
227 : }
228 12100880 : if (airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
229 2952850 : airLoopConv.HVACMassFlowNotConverged[1] = true;
230 2952850 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
231 : }
232 :
233 12100880 : TmpRealARR = airLoopConv.HVACHumSupplyDeck1ToDemandTolValue;
234 12100880 : airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
235 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
236 108907920 : airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
237 : }
238 12100880 : if (airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
239 1686468 : airLoopConv.HVACHumRatNotConverged[1] = true;
240 1686468 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
241 : }
242 :
243 12100880 : TmpRealARR = airLoopConv.HVACTempSupplyDeck1ToDemandTolValue;
244 12100880 : airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
245 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
246 108907920 : airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
247 : }
248 12100880 : if (airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
249 5952485 : airLoopConv.HVACTempNotConverged[1] = true;
250 5952485 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
251 : }
252 :
253 12100880 : TmpRealARR = airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue;
254 12100880 : airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[0] = DeltaEnergy;
255 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
256 108907920 : airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
257 : }
258 12100880 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
259 6329279 : airLoopConv.HVACEnergyNotConverged[1] = true;
260 6329279 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
261 : }
262 :
263 12100880 : TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue;
264 12100880 : airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
265 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
266 108907920 : airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
267 : }
268 12100880 : if (airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
269 4193012 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
270 : }
271 :
272 12100880 : TmpRealARR = airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue;
273 12100880 : airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
274 121008800 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
275 108907920 : airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
276 : }
277 12100880 : if (airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
278 114359 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
279 : }
280 :
281 54875 : } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck2) {
282 :
283 54875 : airLoopConv.HVACMassFlowNotConverged[2] = false;
284 54875 : airLoopConv.HVACHumRatNotConverged[2] = false;
285 54875 : airLoopConv.HVACTempNotConverged[2] = false;
286 54875 : airLoopConv.HVACEnergyNotConverged[2] = false;
287 :
288 54875 : TmpRealARR = airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue;
289 54875 : airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] =
290 54875 : std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
291 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
292 493875 : airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
293 : }
294 54875 : if (airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
295 18396 : airLoopConv.HVACMassFlowNotConverged[2] = true;
296 18396 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
297 : }
298 :
299 54875 : TmpRealARR = airLoopConv.HVACHumSupplyDeck2ToDemandTolValue;
300 54875 : airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
301 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
302 493875 : airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
303 : }
304 54875 : if (airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
305 6704 : airLoopConv.HVACHumRatNotConverged[2] = true;
306 6704 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
307 : }
308 :
309 54875 : TmpRealARR = airLoopConv.HVACTempSupplyDeck2ToDemandTolValue;
310 54875 : airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
311 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
312 493875 : airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
313 : }
314 54875 : if (airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
315 11409 : airLoopConv.HVACTempNotConverged[2] = true;
316 11409 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
317 : }
318 :
319 54875 : TmpRealARR = airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue;
320 54875 : airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[0] = DeltaEnergy;
321 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
322 493875 : airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
323 : }
324 54875 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
325 20349 : airLoopConv.HVACEnergyNotConverged[2] = true;
326 20349 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
327 : }
328 :
329 54875 : TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue;
330 54875 : airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
331 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
332 493875 : airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
333 : }
334 54875 : if (airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
335 12311 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
336 : }
337 :
338 54875 : TmpRealARR = airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue;
339 54875 : airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
340 548750 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
341 493875 : airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
342 : }
343 54875 : if (airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
344 108 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
345 : }
346 : }
347 :
348 : // Always update the new inlet conditions
349 23131543 : thisInletNode.Temp = state.dataLoopNodes->Node(OutletNode).Temp;
350 23131543 : thisInletNode.MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
351 23131543 : thisInletNode.MassFlowRateMinAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail;
352 23131543 : thisInletNode.MassFlowRateMaxAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail;
353 23131543 : thisInletNode.Quality = state.dataLoopNodes->Node(OutletNode).Quality;
354 23131543 : thisInletNode.Press = state.dataLoopNodes->Node(OutletNode).Press;
355 23131543 : thisInletNode.Enthalpy = state.dataLoopNodes->Node(OutletNode).Enthalpy;
356 23131543 : thisInletNode.HumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
357 :
358 23131543 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
359 71296 : thisInletNode.CO2 = state.dataLoopNodes->Node(OutletNode).CO2;
360 : }
361 :
362 23131543 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
363 23301 : thisInletNode.GenContam = state.dataLoopNodes->Node(OutletNode).GenContam;
364 : }
365 : }
366 :
367 34416411 : void UpdatePlantLoopInterface(EnergyPlusData &state,
368 : PlantLocation const &plantLoc, // The 'outlet node' Location
369 : int const ThisLoopSideOutletNode, // Node number for the inlet of the side that needs the outlet node data
370 : int const OtherLoopSideInletNode, // Node number for the outlet of the side of the loop just simulated
371 : bool &OutOfToleranceFlag, // True when the other side of the loop need to be (re)simulated
372 : DataPlant::CommonPipeType const CommonPipeType)
373 : {
374 :
375 : // SUBROUTINE INFORMATION:
376 : // AUTHOR Rick Strand
377 : // DATE WRITTEN October 1998
378 : // MODIFIED na
379 : // RE-ENGINEERED Brent Griffith, Sept. 2010
380 : // RE-ENGINEERED Dan Fisher, Sept. 2010
381 :
382 : // PURPOSE OF THIS SUBROUTINE:
383 : // This subroutine manages any generic HVAC loop interface.
384 :
385 : // METHODOLOGY EMPLOYED:
386 : // This is a simple "forward" interface where all of the properties
387 : // from the outlet of one side of the loop get transfered
388 : // to the inlet node of the corresponding other side of the loop.
389 : // Temperatures are 'lagged' by loop capacitance (i.e. a 'tank')
390 : // between the outlet and inlet nodes.
391 : // the update from the demand side to the supply side always triggers
392 : // resimulation of the supply side if any state variable (or energy) is
393 : // out of tolerance. Remsimulation of the demand side is only triggered if
394 : // flow or energy are out of tolerance. This in effect checks flow and
395 : // ~.25C temperature difference.
396 :
397 : // SUBROUTINE PARAMETER DEFINITIONS:
398 : static constexpr std::string_view RoutineName("UpdatePlantLoopInterface");
399 :
400 34416411 : int LoopNum = plantLoc.loopNum;
401 34416411 : DataPlant::LoopSideLocation ThisLoopSideNum = plantLoc.loopSideNum;
402 34416411 : auto &convergence(state.dataConvergeParams->PlantConvergence(LoopNum));
403 :
404 : // reset out of tolerance flags
405 34416411 : convergence.PlantMassFlowNotConverged = false;
406 34416411 : convergence.PlantTempNotConverged = false;
407 :
408 : // set the LoopSide inlet node
409 34416411 : int ThisLoopSideInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(ThisLoopSideNum).NodeNumIn;
410 :
411 : // save the inlet node temp for DeltaEnergy check
412 34416411 : Real64 OldOtherLoopSideInletMdot = state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate;
413 34416411 : Real64 OldTankOutletTemp = state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
414 :
415 : // calculate the specific heat
416 68832822 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
417 103249233 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, OldTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
418 :
419 : // update the enthalpy
420 34416411 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Enthalpy = Cp * state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
421 :
422 : // update the temperatures and flow rates
423 34416411 : auto &flow_demand_to_supply_tol(convergence.PlantFlowDemandToSupplyTolValue);
424 34416411 : auto &flow_supply_to_demand_tol(convergence.PlantFlowSupplyToDemandTolValue);
425 : Real64 MixedOutletTemp;
426 : Real64 TankOutletTemp;
427 34416411 : if (CommonPipeType == DataPlant::CommonPipeType::Single || CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
428 : // update the temperature
429 520030 : UpdateCommonPipe(state, plantLoc, CommonPipeType, MixedOutletTemp);
430 520030 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = MixedOutletTemp;
431 520030 : TankOutletTemp = MixedOutletTemp;
432 520030 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
433 260015 : rshift1(flow_demand_to_supply_tol);
434 260015 : flow_demand_to_supply_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
435 260015 : if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
436 0 : convergence.PlantMassFlowNotConverged = true;
437 : }
438 : } else {
439 260015 : rshift1(flow_supply_to_demand_tol);
440 260015 : flow_supply_to_demand_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
441 260015 : if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
442 0 : convergence.PlantMassFlowNotConverged = true;
443 : }
444 : }
445 : // Set the flow rate. Continuity requires that the flow rates at the half loop inlet and outlet match
446 520030 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
447 : // Update this LoopSide inlet node Min/MaxAvail to this LoopSide outlet node Min/MaxAvail
448 520030 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMinAvail =
449 520030 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
450 520030 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMaxAvail =
451 520030 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
452 :
453 : } else { // no common pipe
454 33896381 : UpdateHalfLoopInletTemp(state, LoopNum, ThisLoopSideNum, TankOutletTemp);
455 : // update the temperature
456 33896381 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = TankOutletTemp;
457 : // Set the flow tolerance array
458 33896381 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
459 16951963 : rshift1(flow_demand_to_supply_tol);
460 33903926 : flow_demand_to_supply_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
461 16951963 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
462 16951963 : if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
463 1627941 : convergence.PlantMassFlowNotConverged = true;
464 : }
465 : } else {
466 16944418 : rshift1(flow_supply_to_demand_tol);
467 33888836 : flow_supply_to_demand_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
468 16944418 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
469 16944418 : if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
470 538212 : convergence.PlantMassFlowNotConverged = true;
471 : }
472 : }
473 : // PlantFlowTolValue(PlantQuePtr) = ABS(Node(ThisLoopSideOutletNode)%MassFlowRate-Node(OtherLoopSideInletNode)%MassFlowRate)
474 : // Set the flow rate
475 33896381 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
476 : // update the MIN/MAX available flow rates
477 33896381 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMinAvail =
478 33896381 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
479 33896381 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMaxAvail =
480 33896381 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
481 : // update Quality. Note: This update assumes that STEAM cannot be used with common pipes.
482 33896381 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Quality = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Quality;
483 : // pressure update Note: This update assumes that PRESSURE SIMULATION cannot be used with common pipes.
484 33896381 : if (state.dataPlnt->PlantLoop(LoopNum).HasPressureComponents) {
485 : // Don't update pressure, let the pressure simulation handle pressures
486 : } else {
487 : // Do update pressure!
488 33875321 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Press = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Press;
489 : }
490 : }
491 :
492 : // temperature
493 34416411 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
494 17211978 : auto &temp_demand_to_supply_tol(convergence.PlantTempDemandToSupplyTolValue);
495 17211978 : rshift1(temp_demand_to_supply_tol);
496 17211978 : temp_demand_to_supply_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
497 17211978 : if (temp_demand_to_supply_tol[0] > DataConvergParams::PlantTemperatureToler) {
498 4178236 : convergence.PlantTempNotConverged = true;
499 : }
500 : } else {
501 17204433 : auto &temp_supply_to_demand_tol(convergence.PlantTempSupplyToDemandTolValue);
502 17204433 : rshift1(temp_supply_to_demand_tol);
503 17204433 : temp_supply_to_demand_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
504 17204433 : if (temp_supply_to_demand_tol[0] > DataConvergParams::PlantTemperatureToler) {
505 1889298 : convergence.PlantTempNotConverged = true;
506 : }
507 : }
508 :
509 : // Set out of tolerance flags
510 34416411 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
511 17211978 : if (convergence.PlantMassFlowNotConverged || convergence.PlantTempNotConverged) {
512 4503344 : OutOfToleranceFlag = true;
513 : }
514 : } else {
515 17204433 : if (convergence.PlantMassFlowNotConverged) {
516 538212 : OutOfToleranceFlag = true;
517 : }
518 : }
519 34416411 : }
520 :
521 33896381 : void UpdateHalfLoopInletTemp(EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation TankInletLoopSide, Real64 &TankOutletTemp)
522 : {
523 :
524 : // SUBROUTINE INFORMATION:
525 : // AUTHOR Rick Strand
526 : // DATE WRITTEN September 2001
527 : // MODIFIED Simon Rees, July 2007
528 : // Brent Griffith, Feb. 2010, add LoopNum arg
529 : // RE-ENGINEERED Brent Griffith, Sept 2010, generalize for both loop sides
530 : // add pump heat from other loop
531 : // B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
532 :
533 : // PURPOSE OF THIS SUBROUTINE:
534 : // This subroutine calculates the new loop side inlet temperature
535 : // based on the previous temperature of the mixed tank, mass flow rate and the new
536 : // outlet temperature on the supply side. The temperature does not
537 : // pass directly across because the loop has some capacitance. It is
538 : // called separately but used for both supply-to-demand, and demand-to-supply
539 :
540 : // METHODOLOGY EMPLOYED:
541 : // This uses a analytical solution for changes in the
542 : // fluid loop temperature. The user defines some volume of fluid
543 : // for the loop which gets converted to a fixed amount of mass.
544 : // The loop side inlet node is modeled as the outlet of a fully mixed
545 : // tank. Note that this routine is called repeatedly to re calculate
546 : // loop capacitance based on current plant conditions
547 :
548 33896381 : auto &SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
549 33896381 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
550 :
551 : // SUBROUTINE PARAMETER DEFINITIONS:
552 33896381 : Real64 constexpr FracTotLoopMass(0.5); // Fraction of total loop mass assigned to the half loop
553 : static constexpr std::string_view RoutineName("UpdateHalfLoopInletTemp");
554 :
555 : // find tank inlet and outlet nodes
556 33896381 : DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletLoopSide)];
557 33896381 : int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
558 33896381 : Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
559 :
560 : // This needs to be based on time to deal with system downstepping and repeated timesteps
561 33896381 : Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
562 33896381 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
563 6562670 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
564 6562670 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
565 6562670 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
566 : }
567 :
568 33896381 : Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
569 :
570 : // calculate the specific heat for the capacitance calculation
571 67792762 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
572 101689143 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, LastTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
573 : // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
574 :
575 : // calculate new loop inlet temperature. The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
576 : //--half of loop mass. The other half is accounted for at the other half loop interface
577 : //--pump heat. Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
578 : // Pump heat for a dual setpoint loop is added to each loop side inlet
579 : // The previous tank temperature value is used to prevent accumulation of pump heat during iterations while recalculating
580 : // tank conditions each call.
581 : // Analytical solution for ODE, formulated for both final tank temp and average tank temp.
582 :
583 33896381 : Real64 TimeStepSeconds = TimeStepSys * DataGlobalConstants::SecInHour;
584 33896381 : Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
585 33896381 : Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TotalPumpHeat;
586 33896381 : Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
587 : Real64 TankFinalTemp;
588 : Real64 TankAverageTemp;
589 33896381 : if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
590 0 : if (MassFlowRate > 0.0) {
591 0 : TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
592 0 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
593 : } else {
594 0 : TankFinalTemp = LastTankOutletTemp;
595 0 : TankAverageTemp = LastTankOutletTemp;
596 : }
597 :
598 : } else { // tank has mass
599 33896381 : if (MassFlowRate > 0.0) {
600 17946850 : Real64 const mdotCp = MassFlowRate * Cp;
601 17946850 : Real64 const mdotCpTempIn = mdotCp * TankInletTemp;
602 17946850 : Real64 const tankMassCp = ThisTankMass * Cp;
603 17946850 : Real64 const ExponentTerm = mdotCp / tankMassCp * TimeStepSeconds;
604 17946850 : if (ExponentTerm >= 700.0) {
605 0 : TankFinalTemp = (mdotCp * TankInletTemp + PumpHeat) / mdotCp;
606 :
607 0 : TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) / TimeStepSeconds +
608 0 : (mdotCpTempIn + PumpHeat) / mdotCp);
609 : } else {
610 35893700 : TankFinalTemp = (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * std::exp(-ExponentTerm) +
611 17946850 : (mdotCpTempIn + PumpHeat) / (MassFlowRate * Cp);
612 :
613 35893700 : TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * (1.0 - std::exp(-ExponentTerm)) /
614 : TimeStepSeconds +
615 17946850 : (mdotCpTempIn + PumpHeat) / mdotCp);
616 : }
617 : } else {
618 15949531 : TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
619 15949531 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
620 : }
621 : }
622 :
623 : // update last tank outlet temperature
624 33896381 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
625 :
626 : // update heat transport and heat storage rates
627 33896381 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_MdotCpDeltaT =
628 33896381 : (TankInletTemp - TankAverageTemp) * Cp * MassFlowRate;
629 33896381 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_McpDTdt =
630 33896381 : (ThisTankMass * Cp * (TankFinalTemp - LastTankOutletTemp)) / TimeStepSeconds;
631 :
632 : // update report variable
633 33896381 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
634 :
635 33896381 : TankOutletTemp = TankAverageTemp;
636 33896381 : }
637 :
638 520030 : void UpdateCommonPipe(EnergyPlusData &state,
639 : PlantLocation const &TankInletPlantLoc,
640 : DataPlant::CommonPipeType const CommonPipeType,
641 : Real64 &MixedOutletTemp)
642 : {
643 :
644 : // SUBROUTINE INFORMATION:
645 : // AUTHOR Rick Strand
646 : // DATE WRITTEN September 2001
647 : // MODIFIED Simon Rees, July 2007
648 : // Brent Griffith, Feb. 2010, add LoopNum arg
649 : // RE-ENGINEERED Brent Griffith, Sept 2010, generalize for both loop sides
650 : // add pump heat from other loop
651 : // B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
652 :
653 : // PURPOSE OF THIS SUBROUTINE:
654 : // This subroutine calculates the new loop side inlet temperature
655 : // based on the previous temperature of the mixed tank, mass flow rate and the new
656 : // outlet temperature on the supply side. The temperature does not
657 : // pass directly across because the loop has some capacitance. It is
658 : // called separately but used for both supply-to-demand, and demand-to-supply
659 :
660 : // METHODOLOGY EMPLOYED:
661 : // This uses a analytical solution for changes in the
662 : // fluid loop temperature. The user defines some volume of fluid
663 : // for the loop which gets converted to a fixed amount of mass.
664 : // The loop side inlet node is modeled as the outlet of a fully mixed
665 : // tank. Note that this routine is called repeatedly to re calculate
666 : // loop capacitance based on current plant conditions
667 :
668 : // Using/Aliasing
669 520030 : auto &SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
670 520030 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
671 :
672 : // SUBROUTINE PARAMETER DEFINITIONS:
673 : static constexpr std::string_view RoutineName("UpdateCommonPipe");
674 :
675 : // find tank inlet and outlet nodes
676 520030 : int LoopNum = TankInletPlantLoc.loopNum;
677 520030 : DataPlant::LoopSideLocation TankInletLoopSide = TankInletPlantLoc.loopSideNum;
678 520030 : DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletPlantLoc.loopSideNum)]; // Outlet loopside
679 520030 : int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
680 520030 : int TankOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).NodeNumIn;
681 :
682 520030 : Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
683 :
684 : Real64 FracTotLoopMass; // Fraction of total loop mass assigned to the half loop
685 520030 : if (TankInletLoopSide == DataPlant::LoopSideLocation::Demand) {
686 : // for common pipe loops, assume 75% of plant loop volume is on the demand side
687 260015 : FracTotLoopMass = 0.25;
688 : } else {
689 260015 : FracTotLoopMass = 0.75;
690 : }
691 :
692 : // This needs to be based on time to deal with system downstepping and repeated timesteps
693 520030 : Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
694 520030 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
695 33636 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
696 33636 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
697 33636 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
698 : }
699 :
700 520030 : Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
701 :
702 : // calculate the specific heat for the capacitance calculation
703 1040060 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
704 1560090 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, LastTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
705 :
706 : // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
707 :
708 : // calculate new loop inlet temperature. The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
709 : //--half of loop mass. The other half is accounted for at the other half loop interface
710 : //--pump heat. Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
711 : // Pump heat for a dual setpoint loop is added to each loop side inlet
712 : // The previous inlet side temp,'ThisLoopSideTankOutletTemp' is used to prevent accumulation of pump heat during iterations.
713 : // The placement of the 'tank' for common pipes is *after* the outlet node and *before* the flow split or flow mixing.
714 : // This requires no logical check in the code since for purposes of temperature calculations, it is identical to the
715 : // no common pipe case.
716 : // calculation is separated because for common pipe, a different split for mass fraction is applied
717 : // The pump heat source is swapped around here compared to no common pipe (so pump heat sort stays on its own side).
718 520030 : Real64 TimeStepSeconds = TimeStepSys * DataGlobalConstants::SecInHour;
719 520030 : Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
720 520030 : Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).TotalPumpHeat;
721 520030 : Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
722 :
723 : Real64 TankFinalTemp;
724 : Real64 TankAverageTemp;
725 520030 : if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
726 0 : if (MassFlowRate > 0.0) {
727 0 : TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
728 0 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
729 : } else {
730 0 : TankFinalTemp = LastTankOutletTemp;
731 0 : TankAverageTemp = LastTankOutletTemp;
732 : }
733 :
734 : } else { // tank has mass
735 520030 : if (MassFlowRate > 0.0) {
736 762783 : TankFinalTemp = (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
737 254261 : std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds) +
738 254261 : (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp);
739 762783 : TankAverageTemp = ((ThisTankMass * Cp) / (MassFlowRate * Cp) *
740 508522 : (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
741 508522 : (1.0 - std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds)) / TimeStepSeconds +
742 254261 : (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp));
743 : } else {
744 :
745 265769 : TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
746 265769 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
747 : }
748 : }
749 : // Common Pipe Simulation
750 520030 : if (CommonPipeType == DataPlant::CommonPipeType::Single) {
751 137284 : ManageSingleCommonPipe(state, LoopNum, TankOutletLoopSide, TankAverageTemp, MixedOutletTemp);
752 : // 2-way (controlled) common pipe simulation
753 382746 : } else if (CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
754 382746 : PlantLocation TankOutletPlantLoc = {LoopNum, TankOutletLoopSide, 0, 0};
755 :
756 382746 : ManageTwoWayCommonPipe(state, TankOutletPlantLoc, TankAverageTemp);
757 382746 : MixedOutletTemp = state.dataLoopNodes->Node(TankOutletNode).Temp;
758 : }
759 :
760 520030 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
761 :
762 520030 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
763 520030 : }
764 :
765 137284 : void ManageSingleCommonPipe(EnergyPlusData &state,
766 : int const LoopNum, // plant loop number
767 : DataPlant::LoopSideLocation const LoopSide, // plant loop side number
768 : Real64 const TankOutletTemp, // inlet temperature to the common pipe passed in from the capacitance calculation
769 : Real64 &MixedOutletTemp // inlet temperature to the common pipe passed in from the capacitance calculation
770 : )
771 : {
772 :
773 : // SUBROUTINE INFORMATION:
774 : // AUTHOR Sankaranarayanan K P
775 : // DATE WRITTEN November 2006
776 : // MODIFIED B. Griffith, Jan 2010 clean up setup to allow mixing common pipe modes
777 : // B. Griffith, Mar 2010 add LoopNum arg and simplify
778 : // RE-ENGINEERED D. Fisher, Sept. 2010
779 : // B. Griffith, Oct 2011, major rewrite for plant upgrade
780 :
781 : // PURPOSE OF THIS SUBROUTINE:
782 : // To determine the conditions in common pipe viz., the flow flow temperature and direction of flow.
783 :
784 : // METHODOLOGY EMPLOYED:
785 : // Determine the flow on both sides of the common pipe. Decide if flow is coming into common pipe
786 : // or going out of common pipe. After that determine which interface calls the subroutine, i.e. if
787 : // called from "Demand to Supply" interface or "Supply to Demand" interface. Update the node temperatures
788 : // accordingly.
789 :
790 137284 : auto &PlantCommonPipe(state.dataHVACInterfaceMgr->PlantCommonPipe);
791 :
792 : // One time call to set up report variables and set common pipe 'type' flag
793 137284 : if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) SetupCommonPipes(state);
794 :
795 : // fill local node indexes
796 137284 : int NodeNumPriIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
797 137284 : int NodeNumPriOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
798 137284 : int NodeNumSecIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
799 137284 : int NodeNumSecOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
800 :
801 137284 : auto &MyEnvrnFlag(PlantCommonPipe(LoopNum).MyEnvrnFlag);
802 137284 : if (MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
803 13 : PlantCommonPipe(LoopNum).Flow = 0.0;
804 13 : PlantCommonPipe(LoopNum).Temp = 0.0;
805 13 : PlantCommonPipe(LoopNum).FlowDir = NoRecircFlow;
806 13 : MyEnvrnFlag = false;
807 : }
808 137284 : if (!state.dataGlobal->BeginEnvrnFlag) {
809 136444 : MyEnvrnFlag = true;
810 : }
811 :
812 : // every time inits
813 137284 : Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate;
814 137284 : Real64 MdotPri = state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate;
815 :
816 : Real64 TempSecOutTankOut;
817 : Real64 TempPriOutTankOut;
818 137284 : if (LoopSide == DataPlant::LoopSideLocation::Supply) {
819 68642 : TempSecOutTankOut = TankOutletTemp;
820 68642 : TempPriOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
821 : } else {
822 68642 : TempPriOutTankOut = TankOutletTemp;
823 68642 : TempSecOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
824 : }
825 :
826 : // first do mass balances and find common pipe flow rate and direction
827 : Real64 MdotPriRCLeg; // flow rate of primary recirculation thru common pipe kg/s
828 : Real64 MdotSecRCLeg; // flow rate of secondary recirculation thru common pipe kg/s
829 : Real64 TempSecInlet; // temperature at secondary inlet deg C
830 : Real64 TempPriInlet; // temperature at primary inlet deg C
831 : int CPFlowDir; // flow direction in single common pipe
832 : Real64 CommonPipeTemp;
833 137284 : if (MdotPri > MdotSec) {
834 49446 : MdotPriRCLeg = MdotPri - MdotSec;
835 49446 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
836 0 : MdotPriRCLeg = 0.0;
837 0 : CPFlowDir = NoRecircFlow;
838 : } else {
839 49446 : CPFlowDir = PrimaryRecirc;
840 : }
841 49446 : MdotSecRCLeg = 0.0;
842 49446 : CommonPipeTemp = TempPriOutTankOut;
843 87838 : } else if (MdotPri < MdotSec) {
844 5159 : MdotSecRCLeg = MdotSec - MdotPri;
845 5159 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
846 0 : MdotSecRCLeg = 0.0;
847 0 : CPFlowDir = NoRecircFlow;
848 : } else {
849 5159 : CPFlowDir = SecondaryRecirc;
850 : }
851 5159 : MdotPriRCLeg = 0.0;
852 5159 : CommonPipeTemp = TempSecOutTankOut;
853 : } else { // equal
854 82679 : MdotPriRCLeg = 0.0;
855 82679 : MdotSecRCLeg = 0.0;
856 82679 : CPFlowDir = NoRecircFlow;
857 82679 : CommonPipeTemp = (TempPriOutTankOut + TempSecOutTankOut) / 2.0;
858 : }
859 :
860 : // now calculate inlet temps
861 :
862 137284 : if (MdotSec > 0.0) {
863 54486 : TempSecInlet = (MdotPri * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut - MdotPriRCLeg * TempPriOutTankOut) / (MdotSec);
864 : } else {
865 82798 : TempSecInlet = TempPriOutTankOut;
866 : }
867 137284 : if (MdotPri > 0.0) {
868 52186 : TempPriInlet = (MdotSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut - MdotSecRCLeg * TempSecOutTankOut) / (MdotPri);
869 : } else {
870 85098 : TempPriInlet = TempSecOutTankOut;
871 : }
872 :
873 : // Update the Common Pipe Data structure for reporting purposes.
874 137284 : PlantCommonPipe(LoopNum).Flow = max(MdotPriRCLeg, MdotSecRCLeg);
875 137284 : PlantCommonPipe(LoopNum).Temp = CommonPipeTemp;
876 137284 : PlantCommonPipe(LoopNum).FlowDir = CPFlowDir;
877 137284 : state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
878 137284 : state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
879 :
880 137284 : if (LoopSide == DataPlant::LoopSideLocation::Supply) {
881 68642 : MixedOutletTemp = TempPriInlet;
882 : } else {
883 68642 : MixedOutletTemp = TempSecInlet;
884 : }
885 137284 : }
886 :
887 382746 : void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc, Real64 const TankOutletTemp)
888 : {
889 :
890 : // SUBROUTINE INFORMATION:
891 : // AUTHOR B. Griffith
892 : // DATE WRITTEN June 2011
893 : // MODIFIED na
894 : // RE-ENGINEERED B. Griffith, Oct 2011. rewrite
895 :
896 : // PURPOSE OF THIS SUBROUTINE:
897 : // manage two-way common pipe modeling at half-loop interface
898 :
899 : // METHODOLOGY EMPLOYED:
900 : // calculate mixed temperatures and various flow rates
901 : // sequential substitution of system of equations
902 :
903 : // REFERENCES:
904 : // reimplementation of CheckTwoWayCommonPipeConditions by Sankaranarayanan K P Jan 2007
905 :
906 : // SUBROUTINE PARAMETER DEFINITIONS:
907 : enum class UpdateType
908 : {
909 : DemandLedPrimaryInlet,
910 : DemandLedSecondaryInlet,
911 : SupplyLedPrimaryInlet,
912 : SupplyLedSecondaryInlet
913 382746 : } curCallingCase = UpdateType::SupplyLedPrimaryInlet;
914 382746 : constexpr int MaxIterLimitCaseA(8);
915 382746 : constexpr int MaxIterLimitCaseB(4);
916 :
917 : // one time setups
918 382746 : if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) SetupCommonPipes(state);
919 :
920 382746 : auto &plantCommonPipe(state.dataHVACInterfaceMgr->PlantCommonPipe(plantLoc.loopNum));
921 382746 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(plantLoc.loopNum);
922 :
923 : // fill local node indexes
924 382746 : int const NodeNumPriIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
925 382746 : int const NodeNumPriOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
926 382746 : int const NodeNumSecIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
927 382746 : int const NodeNumSecOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
928 :
929 : // begin environment inits
930 382746 : auto &MyEnvrnFlag(plantCommonPipe.MyEnvrnFlag);
931 382746 : if (MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
932 54 : plantCommonPipe.PriToSecFlow = 0.0;
933 54 : plantCommonPipe.SecToPriFlow = 0.0;
934 54 : plantCommonPipe.PriCPLegFlow = 0.0;
935 54 : plantCommonPipe.SecCPLegFlow = 0.0;
936 54 : MyEnvrnFlag = false;
937 : }
938 :
939 382746 : if (!state.dataGlobal->BeginEnvrnFlag) {
940 379554 : MyEnvrnFlag = true;
941 : }
942 :
943 : // every time inits
944 382746 : Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate; // assume known and fixed by demand side operation
945 382746 : Real64 TempCPPrimaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumPriIn).TempSetPoint;
946 382746 : Real64 TempCPSecondaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumSecIn).TempSetPoint;
947 :
948 : // 6 unknowns follow, fill with current values
949 382746 : Real64 MdotPriToSec = plantCommonPipe.PriToSecFlow;
950 382746 : Real64 MdotPriRCLeg = plantCommonPipe.PriCPLegFlow;
951 382746 : Real64 MdotSecRCLeg = plantCommonPipe.SecCPLegFlow;
952 382746 : Real64 TempSecInlet = state.dataLoopNodes->Node(NodeNumSecIn).Temp;
953 382746 : Real64 TempPriInlet = state.dataLoopNodes->Node(NodeNumPriIn).Temp;
954 : Real64 MdotPri =
955 382746 : state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate; // may or may not be an unknown, If variable speed primary side, then unknown
956 :
957 : Real64 TempPriOutTankOut;
958 : Real64 TempSecOutTankOut;
959 382746 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
960 191373 : TempSecOutTankOut = TankOutletTemp;
961 191373 : TempPriOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
962 : } else {
963 191373 : TempPriOutTankOut = TankOutletTemp;
964 191373 : TempSecOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
965 : }
966 :
967 : // determine current case
968 : // which side is being updated
969 : // commonpipe control point is the inlet of one of the half loops
970 382746 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) { // update primary inlet
971 223377 : if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
972 32004 : !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
973 32004 : curCallingCase = UpdateType::SupplyLedPrimaryInlet;
974 :
975 318738 : } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
976 159369 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
977 159369 : curCallingCase = UpdateType::DemandLedPrimaryInlet;
978 : }
979 : } else { // update secondary inlet
980 223377 : if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
981 32004 : !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
982 32004 : curCallingCase = UpdateType::SupplyLedSecondaryInlet;
983 :
984 318738 : } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
985 159369 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
986 159369 : curCallingCase = UpdateType::DemandLedSecondaryInlet;
987 : }
988 : }
989 :
990 382746 : switch (curCallingCase) {
991 64008 : case UpdateType::SupplyLedPrimaryInlet:
992 : case UpdateType::SupplyLedSecondaryInlet:
993 : // CASE A, Primary/Supply Led
994 : // six equations and six unknowns (although one has a setpoint)
995 576072 : for (int loop = 1; loop <= MaxIterLimitCaseA; ++loop) {
996 :
997 : // eq 1
998 512064 : if (std::abs(TempSecOutTankOut - TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
999 512064 : MdotPriToSec = MdotPriRCLeg * (TempCPPrimaryCntrlSetPoint - TempPriOutTankOut) / (TempSecOutTankOut - TempCPPrimaryCntrlSetPoint);
1000 512064 : if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriToSec = 0.0;
1001 512064 : if (MdotPriToSec > MdotSec) MdotPriToSec = MdotSec;
1002 : } else {
1003 0 : MdotPriToSec = MdotSec; // what to do (?)
1004 : }
1005 : // eq. 5
1006 512064 : MdotPriRCLeg = MdotPri - MdotPriToSec;
1007 512064 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriRCLeg = 0.0;
1008 :
1009 : // eq. 4
1010 512064 : MdotSecRCLeg = MdotSec - MdotPriToSec;
1011 512064 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotSecRCLeg = 0.0;
1012 :
1013 : // eq 6
1014 512064 : if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1015 345744 : TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
1016 : } else {
1017 166320 : TempSecInlet = TempPriOutTankOut;
1018 : }
1019 :
1020 : // eq. 3
1021 512064 : if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::SupplyLedPrimaryInlet)) {
1022 : // MdotPri is a variable to be calculated and flow request needs to be made
1023 0 : if (std::abs(TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
1024 :
1025 0 : MdotPri = (MdotPriRCLeg * TempPriOutTankOut + MdotPriToSec * TempSecOutTankOut) / (TempCPPrimaryCntrlSetPoint);
1026 :
1027 0 : if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) MdotPri = 0.0;
1028 : } else {
1029 0 : MdotPri = MdotSec;
1030 : }
1031 0 : PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
1032 0 : PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
1033 : }
1034 :
1035 : // eq. 2
1036 512064 : if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1037 345680 : TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
1038 : } else {
1039 166384 : TempPriInlet = TempSecOutTankOut;
1040 : }
1041 64008 : }
1042 64008 : break;
1043 318738 : case UpdateType::DemandLedPrimaryInlet:
1044 : case UpdateType::DemandLedSecondaryInlet:
1045 : // case B. Secondary/demand led
1046 :
1047 : // six equations and six unknowns (although one has a setpoint)
1048 1593690 : for (int loop = 1; loop <= MaxIterLimitCaseB; ++loop) {
1049 : // eq 1,
1050 1274952 : if (std::abs(TempPriOutTankOut - TempSecOutTankOut) > DataPlant::DeltaTempTol) {
1051 890720 : MdotPriToSec = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempSecOutTankOut);
1052 890720 : if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriToSec = 0.0;
1053 890720 : if (MdotPriToSec > MdotSec) MdotPriToSec = MdotSec;
1054 : } else {
1055 384232 : MdotPriToSec = MdotSec;
1056 : }
1057 :
1058 : // eq. 2,
1059 1274952 : if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1060 631545 : TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
1061 : } else {
1062 643407 : TempPriInlet = TempSecOutTankOut;
1063 : }
1064 :
1065 : // eq. 3
1066 1274952 : if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::DemandLedPrimaryInlet)) {
1067 : // MdotPri is a variable to be calculated and flow request made
1068 0 : if (std::abs(TempPriOutTankOut - TempPriInlet) > DataPlant::DeltaTempTol) {
1069 0 : MdotPri = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempPriInlet);
1070 0 : if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) MdotPri = 0.0;
1071 : } else {
1072 0 : MdotPri = MdotSec;
1073 : }
1074 0 : PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
1075 0 : PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
1076 : }
1077 :
1078 : // eq. 4
1079 1274952 : MdotSecRCLeg = MdotSec - MdotPriToSec;
1080 1274952 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotSecRCLeg = 0.0;
1081 :
1082 : // eq. 5
1083 1274952 : MdotPriRCLeg = MdotPri - MdotPriToSec;
1084 1274952 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriRCLeg = 0.0;
1085 :
1086 : // eq 6
1087 1274952 : if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1088 630624 : TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
1089 : } else {
1090 644328 : TempSecInlet = TempPriOutTankOut;
1091 : }
1092 318738 : }
1093 : }
1094 :
1095 : // update
1096 382746 : plantCommonPipe.PriToSecFlow = MdotPriToSec;
1097 382746 : plantCommonPipe.SecToPriFlow = MdotPriToSec;
1098 382746 : plantCommonPipe.PriCPLegFlow = MdotPriRCLeg;
1099 382746 : plantCommonPipe.SecCPLegFlow = MdotSecRCLeg;
1100 382746 : state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
1101 382746 : state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
1102 382746 : }
1103 :
1104 12 : void SetupCommonPipes(EnergyPlusData &state)
1105 : {
1106 :
1107 : // SUBROUTINE INFORMATION:
1108 : // AUTHOR B. Griffith
1109 : // DATE WRITTEN Jan. 2010
1110 : // MODIFIED B. Griffith Oct. 2011
1111 : // RE-ENGINEERED na
1112 :
1113 : // PURPOSE OF THIS SUBROUTINE:
1114 : // collect allocation, outputs, and other set up for common pipes
1115 :
1116 12 : state.dataHVACInterfaceMgr->PlantCommonPipe.allocate(state.dataPlnt->TotNumLoops);
1117 :
1118 44 : for (int CurLoopNum = 1; CurLoopNum <= state.dataPlnt->TotNumLoops; ++CurLoopNum) {
1119 :
1120 : // reference to easily lookup the first item once
1121 32 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(CurLoopNum);
1122 32 : auto &thisCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(CurLoopNum);
1123 32 : auto &first_demand_component_type(thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1).Type);
1124 32 : auto &first_supply_component_type(thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1).Type);
1125 :
1126 32 : switch (thisPlantLoop.CommonPipeType) {
1127 20 : case DataPlant::CommonPipeType::No:
1128 20 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::No;
1129 20 : break;
1130 3 : case DataPlant::CommonPipeType::Single: // Uncontrolled ('single') common pipe
1131 3 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::Single;
1132 6 : SetupOutputVariable(state,
1133 : "Plant Common Pipe Mass Flow Rate",
1134 : OutputProcessor::Unit::kg_s,
1135 : thisCommonPipe.Flow,
1136 : OutputProcessor::SOVTimeStepType::System,
1137 : OutputProcessor::SOVStoreType::Average,
1138 3 : thisPlantLoop.Name);
1139 6 : SetupOutputVariable(state,
1140 : "Plant Common Pipe Temperature",
1141 : OutputProcessor::Unit::C,
1142 : thisCommonPipe.Temp,
1143 : OutputProcessor::SOVTimeStepType::System,
1144 : OutputProcessor::SOVStoreType::Average,
1145 3 : thisPlantLoop.Name);
1146 6 : SetupOutputVariable(state,
1147 : "Plant Common Pipe Flow Direction Status",
1148 : OutputProcessor::Unit::None,
1149 : thisCommonPipe.FlowDir,
1150 : OutputProcessor::SOVTimeStepType::System,
1151 : OutputProcessor::SOVStoreType::Average,
1152 3 : thisPlantLoop.Name);
1153 :
1154 3 : if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1155 : // If/when the model supports variable-pumping primary, this can be removed.
1156 0 : ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of CommonPipe plant loop");
1157 0 : ShowContinueError(state, "Occurs on plant loop name = " + thisPlantLoop.Name);
1158 0 : ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
1159 0 : ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
1160 : }
1161 3 : break;
1162 9 : case DataPlant::CommonPipeType::TwoWay: // Controlled ('two-way') common pipe
1163 9 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::TwoWay;
1164 18 : SetupOutputVariable(state,
1165 : "Plant Common Pipe Primary Mass Flow Rate",
1166 : OutputProcessor::Unit::kg_s,
1167 : thisCommonPipe.PriCPLegFlow,
1168 : OutputProcessor::SOVTimeStepType::System,
1169 : OutputProcessor::SOVStoreType::Average,
1170 9 : thisPlantLoop.Name);
1171 18 : SetupOutputVariable(state,
1172 : "Plant Common Pipe Secondary Mass Flow Rate",
1173 : OutputProcessor::Unit::kg_s,
1174 : thisCommonPipe.SecCPLegFlow,
1175 : OutputProcessor::SOVTimeStepType::System,
1176 : OutputProcessor::SOVStoreType::Average,
1177 9 : thisPlantLoop.Name);
1178 18 : SetupOutputVariable(state,
1179 : "Plant Common Pipe Primary to Secondary Mass Flow Rate",
1180 : OutputProcessor::Unit::kg_s,
1181 : thisCommonPipe.PriToSecFlow,
1182 : OutputProcessor::SOVTimeStepType::System,
1183 : OutputProcessor::SOVStoreType::Average,
1184 9 : thisPlantLoop.Name);
1185 18 : SetupOutputVariable(state,
1186 : "Plant Common Pipe Secondary to Primary Mass Flow Rate",
1187 : OutputProcessor::Unit::kg_s,
1188 : thisCommonPipe.SecToPriFlow,
1189 : OutputProcessor::SOVTimeStepType::System,
1190 : OutputProcessor::SOVStoreType::Average,
1191 9 : thisPlantLoop.Name);
1192 :
1193 : // check type of pump on supply side inlet
1194 9 : if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
1195 8 : thisCommonPipe.SupplySideInletPumpType = FlowType::Constant;
1196 1 : } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1197 0 : thisCommonPipe.SupplySideInletPumpType = FlowType::Variable;
1198 : // If/when the model supports variable-pumping primary, this can be removed.
1199 0 : ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of TwoWayCommonPipe plant loop");
1200 0 : ShowContinueError(state, "Occurs on plant loop name = " + thisPlantLoop.Name);
1201 0 : ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
1202 0 : ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
1203 : }
1204 : // check type of pump on demand side inlet
1205 9 : if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
1206 0 : thisCommonPipe.DemandSideInletPumpType = FlowType::Constant;
1207 9 : } else if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1208 9 : thisCommonPipe.DemandSideInletPumpType = FlowType::Variable;
1209 : }
1210 9 : break;
1211 0 : default:
1212 0 : assert(false);
1213 : }
1214 : }
1215 :
1216 12 : state.dataHVACInterfaceMgr->CommonPipeSetupFinished = true;
1217 12 : }
1218 :
1219 2313 : } // namespace EnergyPlus::HVACInterfaceManager
|