Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/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 25710548 : 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 25710548 : auto &TmpRealARR = state.dataHVACInterfaceMgr->TmpRealARR;
109 25710548 : auto &airLoopConv = state.dataConvergeParams->AirLoopConvergence(AirLoopNum);
110 25710548 : auto &thisInletNode = state.dataLoopNodes->Node(InletNode);
111 25710548 : int const iCall = (int)CalledFrom;
112 :
113 25710548 : if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode == 0)) {
114 : // 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
115 :
116 15269 : airLoopConv.HVACMassFlowNotConverged[iCall] = false;
117 15269 : airLoopConv.HVACHumRatNotConverged[iCall] = false;
118 15269 : airLoopConv.HVACTempNotConverged[iCall] = false;
119 15269 : airLoopConv.HVACEnergyNotConverged[iCall] = false;
120 :
121 15269 : Real64 totDemandSideMassFlow = 0.0;
122 15269 : Real64 totDemandSideMinAvail = 0.0;
123 15269 : Real64 totDemandSideMaxAvail = 0.0;
124 30538 : for (int demIn = 1; demIn <= state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).NumSupplyNodes; ++demIn) {
125 15269 : int demInNode = state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).ZoneEquipSupplyNodeNum(demIn);
126 15269 : auto const &node = state.dataLoopNodes->Node(demInNode);
127 15269 : totDemandSideMassFlow += node.MassFlowRate;
128 15269 : totDemandSideMinAvail += node.MassFlowRateMinAvail;
129 15269 : totDemandSideMaxAvail += node.MassFlowRateMaxAvail;
130 : }
131 15269 : TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
132 15269 : airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(totDemandSideMassFlow - thisInletNode.MassFlowRate);
133 152690 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
134 137421 : airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
135 : }
136 15269 : if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
137 5242 : airLoopConv.HVACMassFlowNotConverged[iCall] = true;
138 5242 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
139 : }
140 :
141 15269 : thisInletNode.MassFlowRate = totDemandSideMassFlow;
142 15269 : thisInletNode.MassFlowRateMinAvail = totDemandSideMinAvail;
143 15269 : thisInletNode.MassFlowRateMaxAvail = totDemandSideMaxAvail;
144 15269 : return;
145 : }
146 :
147 : // Calculate the approximate energy difference across interface for comparison
148 : Real64 DeltaEnergy =
149 25695279 : DataConvergParams::HVACCpApprox * ((state.dataLoopNodes->Node(OutletNode).MassFlowRate * state.dataLoopNodes->Node(OutletNode).Temp) -
150 25695279 : (thisInletNode.MassFlowRate * thisInletNode.Temp));
151 :
152 25695279 : if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode > 0)) {
153 :
154 12179345 : airLoopConv.HVACMassFlowNotConverged[iCall] = false;
155 12179345 : airLoopConv.HVACHumRatNotConverged[iCall] = false;
156 12179345 : airLoopConv.HVACTempNotConverged[iCall] = false;
157 12179345 : airLoopConv.HVACEnergyNotConverged[iCall] = false;
158 12179345 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
159 40525 : airLoopConv.HVACCO2NotConverged[iCall] = false;
160 : }
161 12179345 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
162 11609 : airLoopConv.HVACGenContamNotConverged[iCall] = false;
163 : }
164 :
165 12179345 : TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
166 12179345 : airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
167 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
168 109614105 : airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
169 : }
170 12179345 : if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
171 3307328 : airLoopConv.HVACMassFlowNotConverged[iCall] = true;
172 3307328 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
173 : }
174 :
175 12179345 : TmpRealARR = airLoopConv.HVACHumDemandToSupplyTolValue;
176 12179345 : airLoopConv.HVACHumDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
177 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
178 109614105 : airLoopConv.HVACHumDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
179 : }
180 12179345 : if (airLoopConv.HVACHumDemandToSupplyTolValue[0] > DataConvergParams::HVACHumRatToler) {
181 447486 : airLoopConv.HVACHumRatNotConverged[iCall] = true;
182 447486 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
183 : }
184 :
185 12179345 : TmpRealARR = airLoopConv.HVACTempDemandToSupplyTolValue;
186 12179345 : airLoopConv.HVACTempDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
187 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
188 109614105 : airLoopConv.HVACTempDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
189 : }
190 12179345 : if (airLoopConv.HVACTempDemandToSupplyTolValue[0] > DataConvergParams::HVACTemperatureToler) {
191 2588728 : airLoopConv.HVACTempNotConverged[iCall] = true;
192 2588728 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
193 : }
194 :
195 12179345 : TmpRealARR = airLoopConv.HVACEnergyDemandToSupplyTolValue;
196 12179345 : airLoopConv.HVACEnergyDemandToSupplyTolValue[0] = std::abs(DeltaEnergy);
197 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
198 109614105 : airLoopConv.HVACEnergyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
199 : }
200 12179345 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
201 4569379 : airLoopConv.HVACEnergyNotConverged[iCall] = true;
202 4569379 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
203 : }
204 :
205 12179345 : TmpRealARR = airLoopConv.HVACEnthalpyDemandToSupplyTolValue;
206 12179345 : airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
207 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
208 109614105 : airLoopConv.HVACEnthalpyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
209 : }
210 12179345 : if (airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
211 1126616 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
212 : }
213 :
214 12179345 : TmpRealARR = airLoopConv.HVACPressureDemandToSupplyTolValue;
215 12179345 : airLoopConv.HVACPressureDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
216 121793450 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
217 109614105 : airLoopConv.HVACPressureDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
218 : }
219 12179345 : if (airLoopConv.HVACPressureDemandToSupplyTolValue[0] > DataConvergParams::HVACPressToler) {
220 12141 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
221 : }
222 :
223 12179345 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
224 40525 : TmpRealARR = airLoopConv.HVACCO2DemandToSupplyTolValue;
225 40525 : airLoopConv.HVACCO2DemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
226 405250 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
227 364725 : airLoopConv.HVACCO2DemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
228 : }
229 40525 : if (airLoopConv.HVACCO2DemandToSupplyTolValue[0] > DataConvergParams::HVACCO2Toler) {
230 9287 : airLoopConv.HVACCO2NotConverged[iCall] = true;
231 9287 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
232 : }
233 : }
234 :
235 12179345 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
236 11609 : TmpRealARR = airLoopConv.HVACGenContamDemandToSupplyTolValue;
237 11609 : airLoopConv.HVACGenContamDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
238 116090 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
239 104481 : airLoopConv.HVACGenContamDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
240 : }
241 11609 : if (airLoopConv.HVACGenContamDemandToSupplyTolValue[0] > DataConvergParams::HVACGenContamToler) {
242 4028 : airLoopConv.HVACGenContamNotConverged[iCall] = true;
243 4028 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
244 : }
245 : }
246 :
247 25695279 : } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck1) {
248 :
249 13461342 : airLoopConv.HVACMassFlowNotConverged[iCall] = false;
250 13461342 : airLoopConv.HVACHumRatNotConverged[iCall] = false;
251 13461342 : airLoopConv.HVACTempNotConverged[iCall] = false;
252 13461342 : airLoopConv.HVACEnergyNotConverged[iCall] = false;
253 13461342 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
254 40486 : airLoopConv.HVACCO2NotConverged[iCall] = false;
255 : }
256 13461342 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
257 11600 : airLoopConv.HVACGenContamNotConverged[iCall] = false;
258 : }
259 :
260 13461342 : TmpRealARR = airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue;
261 13461342 : airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] =
262 13461342 : std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
263 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
264 121152078 : airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
265 : }
266 13461342 : if (airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
267 3228211 : airLoopConv.HVACMassFlowNotConverged[iCall] = true;
268 3228211 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
269 : }
270 :
271 13461342 : TmpRealARR = airLoopConv.HVACHumSupplyDeck1ToDemandTolValue;
272 13461342 : airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
273 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
274 121152078 : airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
275 : }
276 13461342 : if (airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
277 1928971 : airLoopConv.HVACHumRatNotConverged[iCall] = true;
278 1928971 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
279 : }
280 :
281 13461342 : TmpRealARR = airLoopConv.HVACTempSupplyDeck1ToDemandTolValue;
282 13461342 : airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
283 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
284 121152078 : airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
285 : }
286 13461342 : if (airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
287 6524006 : airLoopConv.HVACTempNotConverged[iCall] = true;
288 6524006 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
289 : }
290 :
291 13461342 : TmpRealARR = airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue;
292 13461342 : airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[0] = DeltaEnergy;
293 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
294 121152078 : airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
295 : }
296 13461342 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
297 6849404 : airLoopConv.HVACEnergyNotConverged[iCall] = true;
298 6849404 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
299 : }
300 :
301 13461342 : TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue;
302 13461342 : airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
303 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
304 121152078 : airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
305 : }
306 13461342 : if (airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
307 4540598 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
308 : }
309 :
310 13461342 : TmpRealARR = airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue;
311 13461342 : airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
312 134613420 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
313 121152078 : airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
314 : }
315 13461342 : if (airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
316 368369 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
317 : }
318 : // CO2 check
319 13461342 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
320 40486 : TmpRealARR = airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue;
321 40486 : airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
322 404860 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
323 364374 : airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
324 : }
325 40486 : if (airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACCO2Toler) {
326 9275 : airLoopConv.HVACCO2NotConverged[iCall] = true;
327 9275 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
328 : }
329 : }
330 :
331 13461342 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
332 11600 : TmpRealARR = airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue;
333 11600 : airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[0] =
334 11600 : std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
335 116000 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
336 104400 : airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
337 : }
338 11600 : if (airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACGenContamToler) {
339 2590 : airLoopConv.HVACGenContamNotConverged[iCall] = true;
340 2590 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
341 : }
342 : }
343 :
344 54592 : } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck2) {
345 :
346 54592 : airLoopConv.HVACMassFlowNotConverged[iCall] = false;
347 54592 : airLoopConv.HVACHumRatNotConverged[iCall] = false;
348 54592 : airLoopConv.HVACTempNotConverged[iCall] = false;
349 54592 : airLoopConv.HVACEnergyNotConverged[iCall] = false;
350 54592 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
351 0 : airLoopConv.HVACCO2NotConverged[iCall] = false;
352 : }
353 54592 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
354 0 : airLoopConv.HVACGenContamNotConverged[iCall] = false;
355 : }
356 :
357 54592 : TmpRealARR = airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue;
358 54592 : airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] =
359 54592 : std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
360 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
361 491328 : airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
362 : }
363 54592 : if (airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
364 18390 : airLoopConv.HVACMassFlowNotConverged[iCall] = true;
365 18390 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
366 : }
367 :
368 54592 : TmpRealARR = airLoopConv.HVACHumSupplyDeck2ToDemandTolValue;
369 54592 : airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
370 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
371 491328 : airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
372 : }
373 54592 : if (airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
374 6705 : airLoopConv.HVACHumRatNotConverged[iCall] = true;
375 6705 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
376 : }
377 :
378 54592 : TmpRealARR = airLoopConv.HVACTempSupplyDeck2ToDemandTolValue;
379 54592 : airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
380 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
381 491328 : airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
382 : }
383 54592 : if (airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
384 11282 : airLoopConv.HVACTempNotConverged[iCall] = true;
385 11282 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
386 : }
387 :
388 54592 : TmpRealARR = airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue;
389 54592 : airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[0] = DeltaEnergy;
390 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
391 491328 : airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
392 : }
393 54592 : if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
394 20338 : airLoopConv.HVACEnergyNotConverged[iCall] = true;
395 20338 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
396 : }
397 :
398 54592 : TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue;
399 54592 : airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
400 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
401 491328 : airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
402 : }
403 54592 : if (airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
404 12263 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
405 : }
406 :
407 54592 : TmpRealARR = airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue;
408 54592 : airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
409 545920 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
410 491328 : airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
411 : }
412 54592 : if (airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
413 108 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
414 : }
415 :
416 54592 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
417 0 : TmpRealARR = airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue;
418 0 : airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
419 0 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
420 0 : airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
421 : }
422 0 : if (airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACCO2Toler) {
423 0 : airLoopConv.HVACCO2NotConverged[iCall] = true;
424 0 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
425 : }
426 : }
427 54592 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
428 0 : TmpRealARR = airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue;
429 0 : airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[0] =
430 0 : std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
431 0 : for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
432 0 : airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
433 : }
434 0 : if (airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACGenContamToler) {
435 0 : airLoopConv.HVACGenContamNotConverged[iCall] = true;
436 0 : OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
437 : }
438 : }
439 : }
440 :
441 : // Always update the new inlet conditions
442 25695279 : thisInletNode.Temp = state.dataLoopNodes->Node(OutletNode).Temp;
443 25695279 : thisInletNode.MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
444 25695279 : thisInletNode.MassFlowRateMinAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail;
445 25695279 : thisInletNode.MassFlowRateMaxAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail;
446 25695279 : thisInletNode.Quality = state.dataLoopNodes->Node(OutletNode).Quality;
447 25695279 : thisInletNode.Press = state.dataLoopNodes->Node(OutletNode).Press;
448 25695279 : thisInletNode.Enthalpy = state.dataLoopNodes->Node(OutletNode).Enthalpy;
449 25695279 : thisInletNode.HumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
450 :
451 25695279 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
452 81011 : thisInletNode.CO2 = state.dataLoopNodes->Node(OutletNode).CO2;
453 : }
454 :
455 25695279 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
456 23209 : thisInletNode.GenContam = state.dataLoopNodes->Node(OutletNode).GenContam;
457 : }
458 : }
459 :
460 36200510 : void UpdatePlantLoopInterface(EnergyPlusData &state,
461 : PlantLocation const &plantLoc, // The 'outlet node' Location
462 : int const ThisLoopSideOutletNode, // Node number for the inlet of the side that needs the outlet node data
463 : int const OtherLoopSideInletNode, // Node number for the outlet of the side of the loop just simulated
464 : bool &OutOfToleranceFlag, // True when the other side of the loop need to be (re)simulated
465 : DataPlant::CommonPipeType const CommonPipeType)
466 : {
467 :
468 : // SUBROUTINE INFORMATION:
469 : // AUTHOR Rick Strand
470 : // DATE WRITTEN October 1998
471 : // MODIFIED na
472 : // RE-ENGINEERED Brent Griffith, Sept. 2010
473 : // RE-ENGINEERED Dan Fisher, Sept. 2010
474 :
475 : // PURPOSE OF THIS SUBROUTINE:
476 : // This subroutine manages any generic HVAC loop interface.
477 :
478 : // METHODOLOGY EMPLOYED:
479 : // This is a simple "forward" interface where all of the properties
480 : // from the outlet of one side of the loop get transfered
481 : // to the inlet node of the corresponding other side of the loop.
482 : // Temperatures are 'lagged' by loop capacitance (i.e. a 'tank')
483 : // between the outlet and inlet nodes.
484 : // the update from the demand side to the supply side always triggers
485 : // resimulation of the supply side if any state variable (or energy) is
486 : // out of tolerance. Remsimulation of the demand side is only triggered if
487 : // flow or energy are out of tolerance. This in effect checks flow and
488 : // ~.25C temperature difference.
489 :
490 : // SUBROUTINE PARAMETER DEFINITIONS:
491 : static constexpr std::string_view RoutineName("UpdatePlantLoopInterface");
492 :
493 36200510 : int LoopNum = plantLoc.loopNum;
494 36200510 : DataPlant::LoopSideLocation ThisLoopSideNum = plantLoc.loopSideNum;
495 36200510 : auto &convergence(state.dataConvergeParams->PlantConvergence(LoopNum));
496 :
497 : // reset out of tolerance flags
498 36200510 : convergence.PlantMassFlowNotConverged = false;
499 36200510 : convergence.PlantTempNotConverged = false;
500 :
501 : // set the LoopSide inlet node
502 36200510 : int ThisLoopSideInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(ThisLoopSideNum).NodeNumIn;
503 :
504 : // save the inlet node temp for DeltaEnergy check
505 36200510 : Real64 OldOtherLoopSideInletMdot = state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate;
506 36200510 : Real64 OldTankOutletTemp = state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
507 :
508 : // calculate the specific heat
509 36200510 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
510 36200510 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, OldTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
511 :
512 : // update the enthalpy
513 36200510 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Enthalpy = Cp * state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
514 :
515 : // update the temperatures and flow rates
516 36200510 : auto &flow_demand_to_supply_tol(convergence.PlantFlowDemandToSupplyTolValue);
517 36200510 : auto &flow_supply_to_demand_tol(convergence.PlantFlowSupplyToDemandTolValue);
518 : Real64 MixedOutletTemp;
519 : Real64 TankOutletTemp;
520 36200510 : if (CommonPipeType == DataPlant::CommonPipeType::Single || CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
521 : // update the temperature
522 686714 : UpdateCommonPipe(state, plantLoc, CommonPipeType, MixedOutletTemp);
523 686714 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = MixedOutletTemp;
524 686714 : TankOutletTemp = MixedOutletTemp;
525 686714 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
526 343357 : rshift1(flow_demand_to_supply_tol);
527 343357 : flow_demand_to_supply_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
528 343357 : if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
529 0 : convergence.PlantMassFlowNotConverged = true;
530 : }
531 : } else {
532 343357 : rshift1(flow_supply_to_demand_tol);
533 343357 : flow_supply_to_demand_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
534 343357 : if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
535 0 : convergence.PlantMassFlowNotConverged = true;
536 : }
537 : }
538 : // Set the flow rate. Continuity requires that the flow rates at the half loop inlet and outlet match
539 686714 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
540 : // Update this LoopSide inlet node Min/MaxAvail to this LoopSide outlet node Min/MaxAvail
541 686714 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMinAvail =
542 686714 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
543 686714 : state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMaxAvail =
544 686714 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
545 :
546 : } else { // no common pipe
547 35513796 : UpdateHalfLoopInletTemp(state, LoopNum, ThisLoopSideNum, TankOutletTemp);
548 : // update the temperature
549 35513796 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = TankOutletTemp;
550 : // Set the flow tolerance array
551 35513796 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
552 17761823 : rshift1(flow_demand_to_supply_tol);
553 35523646 : flow_demand_to_supply_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
554 17761823 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
555 17761823 : if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
556 1754073 : convergence.PlantMassFlowNotConverged = true;
557 : }
558 : } else {
559 17751973 : rshift1(flow_supply_to_demand_tol);
560 35503946 : flow_supply_to_demand_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
561 17751973 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
562 17751973 : if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
563 612767 : convergence.PlantMassFlowNotConverged = true;
564 : }
565 : }
566 : // PlantFlowTolValue(PlantQuePtr) = ABS(Node(ThisLoopSideOutletNode)%MassFlowRate-Node(OtherLoopSideInletNode)%MassFlowRate)
567 : // Set the flow rate
568 35513796 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
569 : // update the MIN/MAX available flow rates
570 35513796 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMinAvail =
571 35513796 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
572 35513796 : state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMaxAvail =
573 35513796 : state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
574 : // update Quality. Note: This update assumes that STEAM cannot be used with common pipes.
575 35513796 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Quality = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Quality;
576 : // pressure update Note: This update assumes that PRESSURE SIMULATION cannot be used with common pipes.
577 35513796 : if (state.dataPlnt->PlantLoop(LoopNum).HasPressureComponents) {
578 : // Don't update pressure, let the pressure simulation handle pressures
579 : } else {
580 : // Do update pressure!
581 35492736 : state.dataLoopNodes->Node(OtherLoopSideInletNode).Press = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Press;
582 : }
583 : }
584 :
585 : // temperature
586 36200510 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
587 18105180 : auto &temp_demand_to_supply_tol(convergence.PlantTempDemandToSupplyTolValue);
588 18105180 : rshift1(temp_demand_to_supply_tol);
589 18105180 : temp_demand_to_supply_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
590 18105180 : if (temp_demand_to_supply_tol[0] > DataConvergParams::PlantTemperatureToler) {
591 4341115 : convergence.PlantTempNotConverged = true;
592 : }
593 : } else {
594 18095330 : auto &temp_supply_to_demand_tol(convergence.PlantTempSupplyToDemandTolValue);
595 18095330 : rshift1(temp_supply_to_demand_tol);
596 18095330 : temp_supply_to_demand_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
597 18095330 : if (temp_supply_to_demand_tol[0] > DataConvergParams::PlantTemperatureToler) {
598 1992967 : convergence.PlantTempNotConverged = true;
599 : }
600 : }
601 :
602 : // Set out of tolerance flags
603 36200510 : if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
604 18105180 : if (convergence.PlantMassFlowNotConverged || convergence.PlantTempNotConverged) {
605 4731085 : OutOfToleranceFlag = true;
606 : }
607 : } else {
608 18095330 : if (convergence.PlantMassFlowNotConverged) {
609 612767 : OutOfToleranceFlag = true;
610 : }
611 : }
612 36200510 : }
613 :
614 35513796 : void UpdateHalfLoopInletTemp(EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation TankInletLoopSide, Real64 &TankOutletTemp)
615 : {
616 :
617 : // SUBROUTINE INFORMATION:
618 : // AUTHOR Rick Strand
619 : // DATE WRITTEN September 2001
620 : // MODIFIED Simon Rees, July 2007
621 : // Brent Griffith, Feb. 2010, add LoopNum arg
622 : // RE-ENGINEERED Brent Griffith, Sept 2010, generalize for both loop sides
623 : // add pump heat from other loop
624 : // B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
625 :
626 : // PURPOSE OF THIS SUBROUTINE:
627 : // This subroutine calculates the new loop side inlet temperature
628 : // based on the previous temperature of the mixed tank, mass flow rate and the new
629 : // outlet temperature on the supply side. The temperature does not
630 : // pass directly across because the loop has some capacitance. It is
631 : // called separately but used for both supply-to-demand, and demand-to-supply
632 :
633 : // METHODOLOGY EMPLOYED:
634 : // This uses a analytical solution for changes in the
635 : // fluid loop temperature. The user defines some volume of fluid
636 : // for the loop which gets converted to a fixed amount of mass.
637 : // The loop side inlet node is modeled as the outlet of a fully mixed
638 : // tank. Note that this routine is called repeatedly to re calculate
639 : // loop capacitance based on current plant conditions
640 :
641 35513796 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
642 :
643 : // SUBROUTINE PARAMETER DEFINITIONS:
644 35513796 : Real64 constexpr FracTotLoopMass(0.5); // Fraction of total loop mass assigned to the half loop
645 : static constexpr std::string_view RoutineName("UpdateHalfLoopInletTemp");
646 :
647 : // find tank inlet and outlet nodes
648 35513796 : DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletLoopSide)];
649 35513796 : int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
650 35513796 : Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
651 :
652 : // This needs to be based on time to deal with system downstepping and repeated timesteps
653 35513796 : Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
654 35513796 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
655 6690250 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
656 6690250 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
657 6690250 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
658 : }
659 :
660 35513796 : Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
661 :
662 : // calculate the specific heat for the capacitance calculation
663 35513796 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
664 35513796 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, LastTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
665 : // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
666 :
667 : // calculate new loop inlet temperature. The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
668 : //--half of loop mass. The other half is accounted for at the other half loop interface
669 : //--pump heat. Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
670 : // Pump heat for a dual setpoint loop is added to each loop side inlet
671 : // The previous tank temperature value is used to prevent accumulation of pump heat during iterations while recalculating
672 : // tank conditions each call.
673 : // Analytical solution for ODE, formulated for both final tank temp and average tank temp.
674 :
675 35513796 : Real64 TimeStepSeconds = state.dataHVACGlobal->TimeStepSysSec;
676 35513796 : Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
677 35513796 : Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TotalPumpHeat;
678 35513796 : Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
679 : Real64 TankFinalTemp;
680 : Real64 TankAverageTemp;
681 35513796 : if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
682 0 : if (MassFlowRate > 0.0) {
683 0 : TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
684 0 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
685 : } else {
686 0 : TankFinalTemp = LastTankOutletTemp;
687 0 : TankAverageTemp = LastTankOutletTemp;
688 : }
689 :
690 : } else { // tank has mass
691 35513796 : if (MassFlowRate > 0.0) {
692 18897368 : Real64 const mdotCp = MassFlowRate * Cp;
693 18897368 : Real64 const mdotCpTempIn = mdotCp * TankInletTemp;
694 18897368 : Real64 const tankMassCp = ThisTankMass * Cp;
695 18897368 : Real64 const ExponentTerm = mdotCp / tankMassCp * TimeStepSeconds;
696 18897368 : if (ExponentTerm >= 700.0) {
697 0 : TankFinalTemp = (mdotCp * TankInletTemp + PumpHeat) / mdotCp;
698 :
699 0 : TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) / TimeStepSeconds +
700 0 : (mdotCpTempIn + PumpHeat) / mdotCp);
701 : } else {
702 18897368 : TankFinalTemp = (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * std::exp(-ExponentTerm) +
703 18897368 : (mdotCpTempIn + PumpHeat) / (MassFlowRate * Cp);
704 :
705 18897368 : TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * (1.0 - std::exp(-ExponentTerm)) /
706 : TimeStepSeconds +
707 18897368 : (mdotCpTempIn + PumpHeat) / mdotCp);
708 : }
709 : } else {
710 16616428 : TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
711 16616428 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
712 : }
713 : }
714 :
715 : // update last tank outlet temperature
716 35513796 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
717 :
718 : // update heat transport and heat storage rates
719 35513796 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_MdotCpDeltaT =
720 35513796 : (TankInletTemp - TankAverageTemp) * Cp * MassFlowRate;
721 35513796 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_McpDTdt =
722 35513796 : (ThisTankMass * Cp * (TankFinalTemp - LastTankOutletTemp)) / TimeStepSeconds;
723 :
724 : // update report variable
725 35513796 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
726 :
727 35513796 : TankOutletTemp = TankAverageTemp;
728 35513796 : }
729 :
730 686714 : void UpdateCommonPipe(EnergyPlusData &state,
731 : PlantLocation const &TankInletPlantLoc,
732 : DataPlant::CommonPipeType const CommonPipeType,
733 : Real64 &MixedOutletTemp)
734 : {
735 :
736 : // SUBROUTINE INFORMATION:
737 : // AUTHOR Rick Strand
738 : // DATE WRITTEN September 2001
739 : // MODIFIED Simon Rees, July 2007
740 : // Brent Griffith, Feb. 2010, add LoopNum arg
741 : // RE-ENGINEERED Brent Griffith, Sept 2010, generalize for both loop sides
742 : // add pump heat from other loop
743 : // B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
744 :
745 : // PURPOSE OF THIS SUBROUTINE:
746 : // This subroutine calculates the new loop side inlet temperature
747 : // based on the previous temperature of the mixed tank, mass flow rate and the new
748 : // outlet temperature on the supply side. The temperature does not
749 : // pass directly across because the loop has some capacitance. It is
750 : // called separately but used for both supply-to-demand, and demand-to-supply
751 :
752 : // METHODOLOGY EMPLOYED:
753 : // This uses a analytical solution for changes in the
754 : // fluid loop temperature. The user defines some volume of fluid
755 : // for the loop which gets converted to a fixed amount of mass.
756 : // The loop side inlet node is modeled as the outlet of a fully mixed
757 : // tank. Note that this routine is called repeatedly to re calculate
758 : // loop capacitance based on current plant conditions
759 :
760 : // Using/Aliasing
761 686714 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
762 :
763 : // SUBROUTINE PARAMETER DEFINITIONS:
764 : static constexpr std::string_view RoutineName("UpdateCommonPipe");
765 :
766 : // find tank inlet and outlet nodes
767 686714 : int LoopNum = TankInletPlantLoc.loopNum;
768 686714 : DataPlant::LoopSideLocation TankInletLoopSide = TankInletPlantLoc.loopSideNum;
769 686714 : DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletPlantLoc.loopSideNum)]; // Outlet loopside
770 686714 : int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
771 686714 : int TankOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).NodeNumIn;
772 :
773 686714 : Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
774 :
775 : Real64 FracTotLoopMass; // Fraction of total loop mass assigned to the half loop
776 686714 : if (TankInletLoopSide == DataPlant::LoopSideLocation::Demand) {
777 : // for common pipe loops, assume 75% of plant loop volume is on the demand side
778 343357 : FracTotLoopMass = 0.25;
779 : } else {
780 343357 : FracTotLoopMass = 0.75;
781 : }
782 :
783 : // This needs to be based on time to deal with system downstepping and repeated timesteps
784 686714 : Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
785 686714 : if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
786 44218 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
787 44218 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
788 44218 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
789 : }
790 :
791 686714 : Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
792 :
793 : // calculate the specific heat for the capacitance calculation
794 686714 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(
795 686714 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, LastTankOutletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
796 :
797 : // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
798 :
799 : // calculate new loop inlet temperature. The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
800 : //--half of loop mass. The other half is accounted for at the other half loop interface
801 : //--pump heat. Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
802 : // Pump heat for a dual setpoint loop is added to each loop side inlet
803 : // The previous inlet side temp,'ThisLoopSideTankOutletTemp' is used to prevent accumulation of pump heat during iterations.
804 : // The placement of the 'tank' for common pipes is *after* the outlet node and *before* the flow split or flow mixing.
805 : // This requires no logical check in the code since for purposes of temperature calculations, it is identical to the
806 : // no common pipe case.
807 : // calculation is separated because for common pipe, a different split for mass fraction is applied
808 : // The pump heat source is swapped around here compared to no common pipe (so pump heat sort stays on its own side).
809 686714 : Real64 TimeStepSeconds = state.dataHVACGlobal->TimeStepSysSec;
810 686714 : Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
811 686714 : Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).TotalPumpHeat;
812 686714 : Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
813 :
814 : Real64 TankFinalTemp;
815 : Real64 TankAverageTemp;
816 686714 : if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
817 0 : if (MassFlowRate > 0.0) {
818 0 : TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
819 0 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
820 : } else {
821 0 : TankFinalTemp = LastTankOutletTemp;
822 0 : TankAverageTemp = LastTankOutletTemp;
823 : }
824 :
825 : } else { // tank has mass
826 686714 : if (MassFlowRate > 0.0) {
827 301875 : TankFinalTemp = (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
828 301875 : std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds) +
829 301875 : (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp);
830 301875 : TankAverageTemp = ((ThisTankMass * Cp) / (MassFlowRate * Cp) *
831 301875 : (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
832 301875 : (1.0 - std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds)) / TimeStepSeconds +
833 301875 : (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp));
834 : } else {
835 :
836 384839 : TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
837 384839 : TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
838 : }
839 : }
840 : // Common Pipe Simulation
841 686714 : if (CommonPipeType == DataPlant::CommonPipeType::Single) {
842 261156 : ManageSingleCommonPipe(state, LoopNum, TankOutletLoopSide, TankAverageTemp, MixedOutletTemp);
843 : // 2-way (controlled) common pipe simulation
844 425558 : } else if (CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
845 425558 : PlantLocation TankOutletPlantLoc = {LoopNum, TankOutletLoopSide, 0, 0};
846 :
847 425558 : ManageTwoWayCommonPipe(state, TankOutletPlantLoc, TankAverageTemp);
848 425558 : MixedOutletTemp = state.dataLoopNodes->Node(TankOutletNode).Temp;
849 : }
850 :
851 686714 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
852 :
853 686714 : state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
854 686714 : }
855 :
856 261156 : void ManageSingleCommonPipe(EnergyPlusData &state,
857 : int const LoopNum, // plant loop number
858 : DataPlant::LoopSideLocation const LoopSide, // plant loop side number
859 : Real64 const TankOutletTemp, // inlet temperature to the common pipe passed in from the capacitance calculation
860 : Real64 &MixedOutletTemp // inlet temperature to the common pipe passed in from the capacitance calculation
861 : )
862 : {
863 :
864 : // SUBROUTINE INFORMATION:
865 : // AUTHOR Sankaranarayanan K P
866 : // DATE WRITTEN November 2006
867 : // MODIFIED B. Griffith, Jan 2010 clean up setup to allow mixing common pipe modes
868 : // B. Griffith, Mar 2010 add LoopNum arg and simplify
869 : // RE-ENGINEERED D. Fisher, Sept. 2010
870 : // B. Griffith, Oct 2011, major rewrite for plant upgrade
871 :
872 : // PURPOSE OF THIS SUBROUTINE:
873 : // To determine the conditions in common pipe viz., the flow flow temperature and direction of flow.
874 :
875 : // METHODOLOGY EMPLOYED:
876 : // Determine the flow on both sides of the common pipe. Decide if flow is coming into common pipe
877 : // or going out of common pipe. After that determine which interface calls the subroutine, i.e. if
878 : // called from "Demand to Supply" interface or "Supply to Demand" interface. Update the node temperatures
879 : // accordingly.
880 :
881 : // One time call to set up report variables and set common pipe 'type' flag
882 261156 : if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) SetupCommonPipes(state);
883 :
884 261156 : auto &plantCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(LoopNum);
885 :
886 : // fill local node indexes
887 261156 : int NodeNumPriIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
888 261156 : int NodeNumPriOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
889 261156 : int NodeNumSecIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
890 261156 : int NodeNumSecOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
891 :
892 261156 : if (plantCommonPipe.MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
893 18 : plantCommonPipe.Flow = 0.0;
894 18 : plantCommonPipe.Temp = 0.0;
895 18 : plantCommonPipe.FlowDir = NoRecircFlow;
896 18 : plantCommonPipe.MyEnvrnFlag = false;
897 : }
898 261156 : if (!state.dataGlobal->BeginEnvrnFlag) {
899 259812 : plantCommonPipe.MyEnvrnFlag = true;
900 : }
901 :
902 : // every time inits
903 261156 : Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate;
904 261156 : Real64 MdotPri = state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate;
905 :
906 : Real64 TempSecOutTankOut;
907 : Real64 TempPriOutTankOut;
908 261156 : if (LoopSide == DataPlant::LoopSideLocation::Supply) {
909 130578 : TempSecOutTankOut = TankOutletTemp;
910 130578 : TempPriOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
911 : } else {
912 130578 : TempPriOutTankOut = TankOutletTemp;
913 130578 : TempSecOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
914 : }
915 :
916 : // first do mass balances and find common pipe flow rate and direction
917 : Real64 MdotPriRCLeg; // flow rate of primary recirculation thru common pipe kg/s
918 : Real64 MdotSecRCLeg; // flow rate of secondary recirculation thru common pipe kg/s
919 : Real64 TempSecInlet; // temperature at secondary inlet deg C
920 : Real64 TempPriInlet; // temperature at primary inlet deg C
921 : int CPFlowDir; // flow direction in single common pipe
922 : Real64 CommonPipeTemp;
923 261156 : if (MdotPri > MdotSec) {
924 72349 : MdotPriRCLeg = MdotPri - MdotSec;
925 72349 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
926 0 : MdotPriRCLeg = 0.0;
927 0 : CPFlowDir = NoRecircFlow;
928 : } else {
929 72349 : CPFlowDir = PrimaryRecirc;
930 : }
931 72349 : MdotSecRCLeg = 0.0;
932 72349 : CommonPipeTemp = TempPriOutTankOut;
933 188807 : } else if (MdotPri < MdotSec) {
934 5248 : MdotSecRCLeg = MdotSec - MdotPri;
935 5248 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
936 0 : MdotSecRCLeg = 0.0;
937 0 : CPFlowDir = NoRecircFlow;
938 : } else {
939 5248 : CPFlowDir = SecondaryRecirc;
940 : }
941 5248 : MdotPriRCLeg = 0.0;
942 5248 : CommonPipeTemp = TempSecOutTankOut;
943 : } else { // equal
944 183559 : MdotPriRCLeg = 0.0;
945 183559 : MdotSecRCLeg = 0.0;
946 183559 : CPFlowDir = NoRecircFlow;
947 183559 : CommonPipeTemp = (TempPriOutTankOut + TempSecOutTankOut) / 2.0;
948 : }
949 :
950 : // now calculate inlet temps
951 :
952 261156 : if (MdotSec > 0.0) {
953 78818 : TempSecInlet = (MdotPri * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut - MdotPriRCLeg * TempPriOutTankOut) / (MdotSec);
954 : } else {
955 182338 : TempSecInlet = TempPriOutTankOut;
956 : }
957 261156 : if (MdotPri > 0.0) {
958 76517 : TempPriInlet = (MdotSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut - MdotSecRCLeg * TempSecOutTankOut) / (MdotPri);
959 : } else {
960 184639 : TempPriInlet = TempSecOutTankOut;
961 : }
962 :
963 : // Update the Common Pipe Data structure for reporting purposes.
964 261156 : plantCommonPipe.Flow = max(MdotPriRCLeg, MdotSecRCLeg);
965 261156 : plantCommonPipe.Temp = CommonPipeTemp;
966 261156 : plantCommonPipe.FlowDir = CPFlowDir;
967 261156 : state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
968 261156 : state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
969 :
970 261156 : if (LoopSide == DataPlant::LoopSideLocation::Supply) {
971 130578 : MixedOutletTemp = TempPriInlet;
972 : } else {
973 130578 : MixedOutletTemp = TempSecInlet;
974 : }
975 261156 : }
976 :
977 425558 : void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc, Real64 const TankOutletTemp)
978 : {
979 :
980 : // SUBROUTINE INFORMATION:
981 : // AUTHOR B. Griffith
982 : // DATE WRITTEN June 2011
983 : // MODIFIED na
984 : // RE-ENGINEERED B. Griffith, Oct 2011. rewrite
985 :
986 : // PURPOSE OF THIS SUBROUTINE:
987 : // manage two-way common pipe modeling at half-loop interface
988 :
989 : // METHODOLOGY EMPLOYED:
990 : // calculate mixed temperatures and various flow rates
991 : // sequential substitution of system of equations
992 :
993 : // REFERENCES:
994 : // reimplementation of CheckTwoWayCommonPipeConditions by Sankaranarayanan K P Jan 2007
995 :
996 : // SUBROUTINE PARAMETER DEFINITIONS:
997 : enum class UpdateType
998 : {
999 : DemandLedPrimaryInlet,
1000 : DemandLedSecondaryInlet,
1001 : SupplyLedPrimaryInlet,
1002 : SupplyLedSecondaryInlet
1003 425558 : } curCallingCase = UpdateType::SupplyLedPrimaryInlet;
1004 425558 : constexpr int MaxIterLimitCaseA(8);
1005 425558 : constexpr int MaxIterLimitCaseB(4);
1006 :
1007 : // one time setups
1008 425558 : if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) SetupCommonPipes(state);
1009 :
1010 425558 : auto &plantCommonPipe(state.dataHVACInterfaceMgr->PlantCommonPipe(plantLoc.loopNum));
1011 425558 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(plantLoc.loopNum);
1012 :
1013 : // fill local node indexes
1014 425558 : int const NodeNumPriIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
1015 425558 : int const NodeNumPriOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
1016 425558 : int const NodeNumSecIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
1017 425558 : int const NodeNumSecOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
1018 :
1019 : // begin environment inits
1020 425558 : if (plantCommonPipe.MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
1021 60 : plantCommonPipe.PriToSecFlow = 0.0;
1022 60 : plantCommonPipe.SecToPriFlow = 0.0;
1023 60 : plantCommonPipe.PriCPLegFlow = 0.0;
1024 60 : plantCommonPipe.SecCPLegFlow = 0.0;
1025 60 : plantCommonPipe.MyEnvrnFlag = false;
1026 : }
1027 :
1028 425558 : if (!state.dataGlobal->BeginEnvrnFlag) {
1029 421918 : plantCommonPipe.MyEnvrnFlag = true;
1030 : }
1031 :
1032 : // every time inits
1033 425558 : Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate; // assume known and fixed by demand side operation
1034 425558 : Real64 TempCPPrimaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumPriIn).TempSetPoint;
1035 425558 : Real64 TempCPSecondaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumSecIn).TempSetPoint;
1036 :
1037 : // 6 unknowns follow, fill with current values
1038 425558 : Real64 MdotPriToSec = plantCommonPipe.PriToSecFlow;
1039 425558 : Real64 MdotPriRCLeg = plantCommonPipe.PriCPLegFlow;
1040 425558 : Real64 MdotSecRCLeg = plantCommonPipe.SecCPLegFlow;
1041 425558 : Real64 TempSecInlet = state.dataLoopNodes->Node(NodeNumSecIn).Temp;
1042 425558 : Real64 TempPriInlet = state.dataLoopNodes->Node(NodeNumPriIn).Temp;
1043 : Real64 MdotPri =
1044 425558 : state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate; // may or may not be an unknown, If variable speed primary side, then unknown
1045 :
1046 : Real64 TempPriOutTankOut;
1047 : Real64 TempSecOutTankOut;
1048 425558 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
1049 212779 : TempSecOutTankOut = TankOutletTemp;
1050 212779 : TempPriOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
1051 : } else {
1052 212779 : TempPriOutTankOut = TankOutletTemp;
1053 212779 : TempSecOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
1054 : }
1055 :
1056 : // determine current case
1057 : // which side is being updated
1058 : // commonpipe control point is the inlet of one of the half loops
1059 425558 : if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) { // update primary inlet
1060 244685 : if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
1061 31906 : !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
1062 31906 : curCallingCase = UpdateType::SupplyLedPrimaryInlet;
1063 :
1064 361746 : } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
1065 180873 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
1066 180873 : curCallingCase = UpdateType::DemandLedPrimaryInlet;
1067 : }
1068 : } else { // update secondary inlet
1069 244685 : if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
1070 31906 : !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
1071 31906 : curCallingCase = UpdateType::SupplyLedSecondaryInlet;
1072 :
1073 361746 : } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
1074 180873 : thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
1075 180873 : curCallingCase = UpdateType::DemandLedSecondaryInlet;
1076 : }
1077 : }
1078 :
1079 425558 : switch (curCallingCase) {
1080 63812 : case UpdateType::SupplyLedPrimaryInlet:
1081 : case UpdateType::SupplyLedSecondaryInlet:
1082 : // CASE A, Primary/Supply Led
1083 : // six equations and six unknowns (although one has a setpoint)
1084 574308 : for (int loop = 1; loop <= MaxIterLimitCaseA; ++loop) {
1085 :
1086 : // eq 1
1087 510496 : if (std::abs(TempSecOutTankOut - TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
1088 510496 : MdotPriToSec = MdotPriRCLeg * (TempCPPrimaryCntrlSetPoint - TempPriOutTankOut) / (TempSecOutTankOut - TempCPPrimaryCntrlSetPoint);
1089 510496 : if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriToSec = 0.0;
1090 510496 : if (MdotPriToSec > MdotSec) MdotPriToSec = MdotSec;
1091 : } else {
1092 0 : MdotPriToSec = MdotSec; // what to do (?)
1093 : }
1094 : // eq. 5
1095 510496 : MdotPriRCLeg = MdotPri - MdotPriToSec;
1096 510496 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriRCLeg = 0.0;
1097 :
1098 : // eq. 4
1099 510496 : MdotSecRCLeg = MdotSec - MdotPriToSec;
1100 510496 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotSecRCLeg = 0.0;
1101 :
1102 : // eq 6
1103 510496 : if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1104 344176 : TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
1105 : } else {
1106 166320 : TempSecInlet = TempPriOutTankOut;
1107 : }
1108 :
1109 : // eq. 3
1110 510496 : if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::SupplyLedPrimaryInlet)) {
1111 : // MdotPri is a variable to be calculated and flow request needs to be made
1112 0 : if (std::abs(TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
1113 :
1114 0 : MdotPri = (MdotPriRCLeg * TempPriOutTankOut + MdotPriToSec * TempSecOutTankOut) / (TempCPPrimaryCntrlSetPoint);
1115 :
1116 0 : if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) MdotPri = 0.0;
1117 : } else {
1118 0 : MdotPri = MdotSec;
1119 : }
1120 0 : PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
1121 0 : PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
1122 : }
1123 :
1124 : // eq. 2
1125 510496 : if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1126 344112 : TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
1127 : } else {
1128 166384 : TempPriInlet = TempSecOutTankOut;
1129 : }
1130 : }
1131 63812 : break;
1132 361746 : case UpdateType::DemandLedPrimaryInlet:
1133 : case UpdateType::DemandLedSecondaryInlet:
1134 : // case B. Secondary/demand led
1135 :
1136 : // six equations and six unknowns (although one has a setpoint)
1137 1808730 : for (int loop = 1; loop <= MaxIterLimitCaseB; ++loop) {
1138 : // eq 1,
1139 1446984 : if (std::abs(TempPriOutTankOut - TempSecOutTankOut) > DataPlant::DeltaTempTol) {
1140 1062808 : MdotPriToSec = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempSecOutTankOut);
1141 1062808 : if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriToSec = 0.0;
1142 1062808 : if (MdotPriToSec > MdotSec) MdotPriToSec = MdotSec;
1143 : } else {
1144 384176 : MdotPriToSec = MdotSec;
1145 : }
1146 :
1147 : // eq. 2,
1148 1446984 : if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1149 725457 : TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
1150 : } else {
1151 721527 : TempPriInlet = TempSecOutTankOut;
1152 : }
1153 :
1154 : // eq. 3
1155 1446984 : if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::DemandLedPrimaryInlet)) {
1156 : // MdotPri is a variable to be calculated and flow request made
1157 0 : if (std::abs(TempPriOutTankOut - TempPriInlet) > DataPlant::DeltaTempTol) {
1158 0 : MdotPri = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempPriInlet);
1159 0 : if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) MdotPri = 0.0;
1160 : } else {
1161 0 : MdotPri = MdotSec;
1162 : }
1163 0 : PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
1164 0 : PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
1165 : }
1166 :
1167 : // eq. 4
1168 1446984 : MdotSecRCLeg = MdotSec - MdotPriToSec;
1169 1446984 : if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotSecRCLeg = 0.0;
1170 :
1171 : // eq. 5
1172 1446984 : MdotPriRCLeg = MdotPri - MdotPriToSec;
1173 1446984 : if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) MdotPriRCLeg = 0.0;
1174 :
1175 : // eq 6
1176 1446984 : if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
1177 724536 : TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
1178 : } else {
1179 722448 : TempSecInlet = TempPriOutTankOut;
1180 : }
1181 : }
1182 : }
1183 :
1184 : // update
1185 425558 : plantCommonPipe.PriToSecFlow = MdotPriToSec;
1186 425558 : plantCommonPipe.SecToPriFlow = MdotPriToSec;
1187 425558 : plantCommonPipe.PriCPLegFlow = MdotPriRCLeg;
1188 425558 : plantCommonPipe.SecCPLegFlow = MdotSecRCLeg;
1189 425558 : state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
1190 425558 : state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
1191 425558 : }
1192 :
1193 14 : void SetupCommonPipes(EnergyPlusData &state)
1194 : {
1195 :
1196 : // SUBROUTINE INFORMATION:
1197 : // AUTHOR B. Griffith
1198 : // DATE WRITTEN Jan. 2010
1199 : // MODIFIED B. Griffith Oct. 2011
1200 : // RE-ENGINEERED na
1201 :
1202 : // PURPOSE OF THIS SUBROUTINE:
1203 : // collect allocation, outputs, and other set up for common pipes
1204 :
1205 14 : state.dataHVACInterfaceMgr->PlantCommonPipe.allocate(state.dataPlnt->TotNumLoops);
1206 :
1207 52 : for (int CurLoopNum = 1; CurLoopNum <= state.dataPlnt->TotNumLoops; ++CurLoopNum) {
1208 :
1209 : // reference to easily lookup the first item once
1210 38 : auto &thisPlantLoop = state.dataPlnt->PlantLoop(CurLoopNum);
1211 38 : auto &thisCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(CurLoopNum);
1212 38 : auto const &first_demand_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1).Type;
1213 38 : auto const &first_supply_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1).Type;
1214 :
1215 38 : switch (thisPlantLoop.CommonPipeType) {
1216 24 : case DataPlant::CommonPipeType::No:
1217 24 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::No;
1218 24 : break;
1219 4 : case DataPlant::CommonPipeType::Single: // Uncontrolled ('single') common pipe
1220 4 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::Single;
1221 8 : SetupOutputVariable(state,
1222 : "Plant Common Pipe Mass Flow Rate",
1223 : Constant::Units::kg_s,
1224 4 : thisCommonPipe.Flow,
1225 : OutputProcessor::TimeStepType::System,
1226 : OutputProcessor::StoreType::Average,
1227 4 : thisPlantLoop.Name);
1228 8 : SetupOutputVariable(state,
1229 : "Plant Common Pipe Temperature",
1230 : Constant::Units::C,
1231 4 : thisCommonPipe.Temp,
1232 : OutputProcessor::TimeStepType::System,
1233 : OutputProcessor::StoreType::Average,
1234 4 : thisPlantLoop.Name);
1235 4 : SetupOutputVariable(state,
1236 : "Plant Common Pipe Flow Direction Status",
1237 : Constant::Units::None,
1238 4 : thisCommonPipe.FlowDir,
1239 : OutputProcessor::TimeStepType::System,
1240 : OutputProcessor::StoreType::Average,
1241 4 : thisPlantLoop.Name);
1242 :
1243 4 : if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1244 : // If/when the model supports variable-pumping primary, this can be removed.
1245 1 : ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of CommonPipe plant loop");
1246 1 : ShowContinueError(state, format("Occurs on plant loop name = {}", thisPlantLoop.Name));
1247 1 : ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
1248 1 : ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
1249 : }
1250 4 : break;
1251 10 : case DataPlant::CommonPipeType::TwoWay: // Controlled ('two-way') common pipe
1252 10 : thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::TwoWay;
1253 20 : SetupOutputVariable(state,
1254 : "Plant Common Pipe Primary Mass Flow Rate",
1255 : Constant::Units::kg_s,
1256 10 : thisCommonPipe.PriCPLegFlow,
1257 : OutputProcessor::TimeStepType::System,
1258 : OutputProcessor::StoreType::Average,
1259 10 : thisPlantLoop.Name);
1260 20 : SetupOutputVariable(state,
1261 : "Plant Common Pipe Secondary Mass Flow Rate",
1262 : Constant::Units::kg_s,
1263 10 : thisCommonPipe.SecCPLegFlow,
1264 : OutputProcessor::TimeStepType::System,
1265 : OutputProcessor::StoreType::Average,
1266 10 : thisPlantLoop.Name);
1267 20 : SetupOutputVariable(state,
1268 : "Plant Common Pipe Primary to Secondary Mass Flow Rate",
1269 : Constant::Units::kg_s,
1270 10 : thisCommonPipe.PriToSecFlow,
1271 : OutputProcessor::TimeStepType::System,
1272 : OutputProcessor::StoreType::Average,
1273 10 : thisPlantLoop.Name);
1274 20 : SetupOutputVariable(state,
1275 : "Plant Common Pipe Secondary to Primary Mass Flow Rate",
1276 : Constant::Units::kg_s,
1277 10 : thisCommonPipe.SecToPriFlow,
1278 : OutputProcessor::TimeStepType::System,
1279 : OutputProcessor::StoreType::Average,
1280 10 : thisPlantLoop.Name);
1281 :
1282 : // check type of pump on supply side inlet
1283 10 : if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
1284 9 : thisCommonPipe.SupplySideInletPumpType = FlowType::Constant;
1285 1 : } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1286 0 : thisCommonPipe.SupplySideInletPumpType = FlowType::Variable;
1287 : // If/when the model supports variable-pumping primary, this can be removed.
1288 0 : ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of TwoWayCommonPipe plant loop");
1289 0 : ShowContinueError(state, format("Occurs on plant loop name = {}", thisPlantLoop.Name));
1290 0 : ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
1291 0 : ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
1292 : }
1293 : // check type of pump on demand side inlet
1294 10 : if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
1295 0 : thisCommonPipe.DemandSideInletPumpType = FlowType::Constant;
1296 10 : } else if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
1297 10 : thisCommonPipe.DemandSideInletPumpType = FlowType::Variable;
1298 : }
1299 10 : break;
1300 0 : default:
1301 0 : assert(false);
1302 : }
1303 : }
1304 :
1305 14 : state.dataHVACInterfaceMgr->CommonPipeSetupFinished = true;
1306 14 : }
1307 :
1308 : } // namespace EnergyPlus::HVACInterfaceManager
|