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 : // EnergyPlus Headers
52 : #include <EnergyPlus/Data/EnergyPlusData.hh>
53 : #include <EnergyPlus/DataContaminantBalance.hh>
54 : #include <EnergyPlus/DataEnvironment.hh>
55 : #include <EnergyPlus/DataLoopNode.hh>
56 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
57 : #include <EnergyPlus/NodeInputManager.hh>
58 : #include <EnergyPlus/Psychrometrics.hh>
59 : #include <EnergyPlus/SplitterComponent.hh>
60 : #include <EnergyPlus/UtilityRoutines.hh>
61 :
62 : namespace EnergyPlus {
63 :
64 : namespace SplitterComponent {
65 : // Module containing the Splitter simulation routines
66 :
67 : // MODULE INFORMATION:
68 : // AUTHOR Richard J. Liesen
69 : // DATE WRITTEN March 2000
70 : // MODIFIED na
71 : // RE-ENGINEERED na
72 :
73 : // PURPOSE OF THIS MODULE:
74 : // To encapsulate the data and algorithms required to
75 : // manage Air Path Splitter Components
76 :
77 : using namespace DataLoopNode;
78 :
79 20307326 : void SimAirLoopSplitter(EnergyPlusData &state,
80 : std::string_view CompName,
81 : bool const FirstHVACIteration,
82 : bool const FirstCall,
83 : bool &SplitterInletChanged,
84 : int &CompIndex)
85 : {
86 :
87 : // SUBROUTINE INFORMATION:
88 : // AUTHOR Richard Liesen
89 : // DATE WRITTEN March 2000
90 : // MODIFIED na
91 : // RE-ENGINEERED na
92 :
93 : // PURPOSE OF THIS SUBROUTINE:
94 : // This subroutine manages Splitter component simulation.
95 : // It is called from the SimAirLoopComponent
96 : // at the system time step.
97 :
98 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
99 : int SplitterNum; // The Splitter that you are currently loading input for
100 :
101 : // Obtains and Allocates Splitter related parameters from input file
102 20307326 : if (state.dataSplitterComponent->GetSplitterInputFlag) { // First time subroutine has been entered
103 522 : GetSplitterInput(state);
104 : }
105 :
106 : // Find the correct SplitterNumber
107 20307326 : if (CompIndex == 0) {
108 1180 : SplitterNum = UtilityRoutines::FindItemInList(CompName, state.dataSplitterComponent->SplitterCond, &SplitterConditions::SplitterName);
109 1180 : if (SplitterNum == 0) {
110 0 : ShowFatalError(state, "SimAirLoopSplitter: Splitter not found=" + std::string{CompName});
111 : }
112 1180 : CompIndex = SplitterNum;
113 : } else {
114 20306146 : SplitterNum = CompIndex;
115 20306146 : if (SplitterNum > state.dataSplitterComponent->NumSplitters || SplitterNum < 1) {
116 0 : ShowFatalError(state,
117 0 : format("SimAirLoopSplitter: Invalid CompIndex passed={}, Number of Splitters={}, Splitter name={}",
118 : SplitterNum,
119 0 : state.dataSplitterComponent->NumSplitters,
120 0 : CompName));
121 : }
122 20306146 : if (state.dataSplitterComponent->CheckEquipName(SplitterNum)) {
123 1180 : if (CompName != state.dataSplitterComponent->SplitterCond(SplitterNum).SplitterName) {
124 0 : ShowFatalError(state,
125 0 : format("SimAirLoopSplitter: Invalid CompIndex passed={}, Splitter name={}, stored Splitter Name for that index={}",
126 : SplitterNum,
127 : CompName,
128 0 : state.dataSplitterComponent->SplitterCond(SplitterNum).SplitterName));
129 : }
130 1180 : state.dataSplitterComponent->CheckEquipName(SplitterNum) = false;
131 : }
132 : }
133 :
134 20307326 : InitAirLoopSplitter(state, SplitterNum, FirstHVACIteration, FirstCall); // Initialize all Splitter related parameters
135 :
136 20307326 : CalcAirLoopSplitter(state, SplitterNum, FirstCall);
137 :
138 : // Update the current Splitter to the outlet nodes
139 20307326 : UpdateSplitter(state, SplitterNum, SplitterInletChanged, FirstCall);
140 :
141 : // Report the current Splitter
142 20307326 : ReportSplitter(SplitterNum);
143 20307326 : }
144 :
145 : //*******************************
146 :
147 : // Get Input Section of the Module
148 : //******************************************************************************
149 :
150 522 : void GetSplitterInput(EnergyPlusData &state)
151 : {
152 :
153 : // SUBROUTINE INFORMATION:
154 : // AUTHOR Richard J. Liesen
155 : // DATE WRITTEN March 2000
156 : // MODIFIED na
157 : // RE-ENGINEERED na
158 :
159 : // PURPOSE OF THIS SUBROUTINE:
160 : // This subroutine is the main routine to call other input routines and
161 : // Get routines. The Splitter only gets node connection data and not mass
162 : // flow rates.
163 :
164 : // METHODOLOGY EMPLOYED:
165 : // Uses the status flags to trigger events.
166 :
167 : // Using/Aliasing
168 : using NodeInputManager::GetOnlySingleNode;
169 :
170 : // SUBROUTINE PARAMETER DEFINITIONS:
171 : static constexpr std::string_view RoutineName("GetSplitterInput: "); // include trailing blank space
172 :
173 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
174 : int SplitterNum; // The Splitter that you are currently loading input into
175 : int NumAlphas;
176 : int NumNums;
177 : int NodeNum;
178 : int IOStat;
179 522 : bool ErrorsFound(false);
180 : int NumParams;
181 : int OutNodeNum1;
182 : int OutNodeNum2;
183 1044 : std::string CurrentModuleObject; // for ease in getting objects
184 1044 : Array1D_string AlphArray; // Alpha input items for object
185 1044 : Array1D_string cAlphaFields; // Alpha field names
186 1044 : Array1D_string cNumericFields; // Numeric field names
187 1044 : Array1D<Real64> NumArray; // Numeric input items for object
188 1044 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
189 1044 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
190 :
191 : // RESET THE GETINPUT FLAG
192 522 : state.dataSplitterComponent->GetSplitterInputFlag = false;
193 :
194 522 : CurrentModuleObject = "AirLoopHVAC:ZoneSplitter";
195 522 : state.dataSplitterComponent->NumSplitters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
196 :
197 522 : if (state.dataSplitterComponent->NumSplitters > 0)
198 522 : state.dataSplitterComponent->SplitterCond.allocate(state.dataSplitterComponent->NumSplitters);
199 522 : state.dataSplitterComponent->CheckEquipName.dimension(state.dataSplitterComponent->NumSplitters, true);
200 :
201 522 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumParams, NumAlphas, NumNums);
202 522 : AlphArray.allocate(NumAlphas);
203 522 : cAlphaFields.allocate(NumAlphas);
204 522 : lAlphaBlanks.dimension(NumAlphas, true);
205 522 : cNumericFields.allocate(NumNums);
206 522 : lNumericBlanks.dimension(NumNums, true);
207 522 : NumArray.dimension(NumNums, 0.0);
208 :
209 1702 : for (SplitterNum = 1; SplitterNum <= state.dataSplitterComponent->NumSplitters; ++SplitterNum) {
210 1180 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
211 : CurrentModuleObject,
212 : SplitterNum,
213 : AlphArray,
214 : NumAlphas,
215 : NumArray,
216 : NumNums,
217 : IOStat,
218 : lNumericBlanks,
219 : lAlphaBlanks,
220 : cAlphaFields,
221 : cNumericFields);
222 1180 : UtilityRoutines::IsNameEmpty(state, AlphArray(1), CurrentModuleObject, ErrorsFound);
223 :
224 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).SplitterName = AlphArray(1);
225 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode =
226 2360 : GetOnlySingleNode(state,
227 1180 : AlphArray(2),
228 : ErrorsFound,
229 : DataLoopNode::ConnectionObjectType::AirLoopHVACZoneSplitter,
230 1180 : AlphArray(1),
231 : DataLoopNode::NodeFluidType::Air,
232 : DataLoopNode::ConnectionType::Inlet,
233 : NodeInputManager::CompFluidStream::Primary,
234 1180 : ObjectIsNotParent);
235 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes = NumAlphas - 2;
236 :
237 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
238 1180 : .OutletNode.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
239 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
240 1180 : .OutletMassFlowRate.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
241 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
242 1180 : .OutletMassFlowRateMaxAvail.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
243 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
244 1180 : .OutletMassFlowRateMinAvail.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
245 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
246 1180 : .OutletTemp.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
247 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
248 1180 : .OutletHumRat.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
249 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
250 1180 : .OutletEnthalpy.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
251 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum)
252 1180 : .OutletPressure.allocate(state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes);
253 :
254 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRate = 0.0;
255 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMaxAvail = 0.0;
256 1180 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMinAvail = 0.0;
257 :
258 4660 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
259 :
260 3480 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum) =
261 6960 : GetOnlySingleNode(state,
262 3480 : AlphArray(2 + NodeNum),
263 : ErrorsFound,
264 : DataLoopNode::ConnectionObjectType::AirLoopHVACZoneSplitter,
265 3480 : AlphArray(1),
266 : DataLoopNode::NodeFluidType::Air,
267 : DataLoopNode::ConnectionType::Outlet,
268 : NodeInputManager::CompFluidStream::Primary,
269 3480 : ObjectIsNotParent);
270 3480 : if (lAlphaBlanks(2 + NodeNum)) {
271 0 : ShowSevereError(state, cAlphaFields(2 + NodeNum) + " is Blank, " + CurrentModuleObject + " = " + AlphArray(1));
272 0 : ErrorsFound = true;
273 : }
274 : }
275 :
276 : } // end Number of Splitter Loop
277 :
278 : // Check for duplicate names specified in Zone Splitter
279 1702 : for (SplitterNum = 1; SplitterNum <= state.dataSplitterComponent->NumSplitters; ++SplitterNum) {
280 1180 : NodeNum = state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode;
281 4660 : for (OutNodeNum1 = 1; OutNodeNum1 <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutNodeNum1) {
282 3480 : if (NodeNum != state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(OutNodeNum1)) continue;
283 0 : ShowSevereError(state,
284 0 : CurrentModuleObject + " = " + state.dataSplitterComponent->SplitterCond(SplitterNum).SplitterName +
285 : " specifies an outlet node name the same as the inlet node.");
286 0 : ShowContinueError(state, ".." + cAlphaFields(2) + '=' + state.dataLoopNodes->NodeID(NodeNum));
287 0 : ShowContinueError(state, format("..Outlet Node #{} is duplicate.", OutNodeNum1));
288 0 : ErrorsFound = true;
289 : }
290 4660 : for (OutNodeNum1 = 1; OutNodeNum1 <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutNodeNum1) {
291 17618 : for (OutNodeNum2 = OutNodeNum1 + 1; OutNodeNum2 <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes;
292 : ++OutNodeNum2) {
293 28276 : if (state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(OutNodeNum1) !=
294 14138 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(OutNodeNum2))
295 14138 : continue;
296 0 : ShowSevereError(state,
297 0 : CurrentModuleObject + " = " + state.dataSplitterComponent->SplitterCond(SplitterNum).SplitterName +
298 : " specifies duplicate outlet nodes in its outlet node list.");
299 0 : ShowContinueError(state, format("..Outlet Node #{} Name={}", OutNodeNum1, state.dataLoopNodes->NodeID(OutNodeNum1)));
300 0 : ShowContinueError(state, format("..Outlet Node #{} is duplicate.", OutNodeNum2));
301 0 : ErrorsFound = true;
302 : }
303 : }
304 : }
305 :
306 522 : AlphArray.deallocate();
307 522 : NumArray.deallocate();
308 522 : cAlphaFields.deallocate();
309 522 : lAlphaBlanks.deallocate();
310 522 : cNumericFields.deallocate();
311 522 : lNumericBlanks.deallocate();
312 :
313 522 : if (ErrorsFound) {
314 0 : ShowFatalError(state, std::string{RoutineName} + "Errors found in getting input.");
315 : }
316 522 : }
317 :
318 20307326 : void InitAirLoopSplitter(EnergyPlusData &state, int const SplitterNum, bool const FirstHVACIteration, bool const FirstCall)
319 : {
320 :
321 : // SUBROUTINE INFORMATION:
322 : // AUTHOR Richard J. Liesen
323 : // DATE WRITTEN March 2000
324 : // MODIFIED na
325 : // RE-ENGINEERED na
326 :
327 : // PURPOSE OF THIS SUBROUTINE:
328 : // This subroutine is for initialisations of the Splitter Components.
329 :
330 : // METHODOLOGY EMPLOYED:
331 : // Uses the status flags to trigger events.
332 :
333 : using Psychrometrics::PsyHFnTdbW;
334 :
335 : int InletNode;
336 : int OutletNode;
337 : int NodeNum;
338 : Real64 AirEnthalpy; // [J/kg]
339 :
340 : // Do the Begin Environment initializations
341 20307326 : if (state.dataGlobal->BeginEnvrnFlag && state.dataSplitterComponent->MyEnvrnFlag) {
342 :
343 : // Calculate the air density and enthalpy for standard conditions...
344 3108 : AirEnthalpy = PsyHFnTdbW(20.0, state.dataEnvrn->OutHumRat);
345 :
346 : // Initialize the inlet node to s standard set of conditions so that the
347 : // flows match around the loop & do not cause convergence problems.
348 3108 : InletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode;
349 3108 : state.dataLoopNodes->Node(InletNode).Temp = 20.0;
350 3108 : state.dataLoopNodes->Node(InletNode).HumRat = state.dataEnvrn->OutHumRat;
351 3108 : state.dataLoopNodes->Node(InletNode).Enthalpy = AirEnthalpy;
352 3108 : state.dataLoopNodes->Node(InletNode).Press = state.dataEnvrn->OutBaroPress;
353 3108 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
354 38 : state.dataLoopNodes->Node(InletNode).CO2 = state.dataContaminantBalance->OutdoorCO2;
355 : }
356 3108 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
357 11 : state.dataLoopNodes->Node(InletNode).GenContam = state.dataContaminantBalance->OutdoorGC;
358 : }
359 :
360 3108 : state.dataSplitterComponent->MyEnvrnFlag = false;
361 : }
362 :
363 20307326 : if (!state.dataGlobal->BeginEnvrnFlag) {
364 20204304 : state.dataSplitterComponent->MyEnvrnFlag = true;
365 : }
366 :
367 : // Set the inlet node for the Splitter
368 20307326 : InletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode;
369 :
370 : // Do the following initializations (every time step): This should be the info from
371 : // the previous components outlets or the node data in this section.
372 : // Load the node data in this section for the component simulation
373 :
374 : // This section is very important to understand. The system off condition is important
375 : // transfer around the loop even if the splitter does not have enough information to
376 : // calculate the correct flow rates since the dampers are downstream and there is no pressure
377 : // simulation. What happens in this section is the flow from upstream is not zero is
378 : // arbitrarily split by the number of inlet nodes. This is by no way meant to determine the
379 : // correct split flow! Just to give each outlet a non-zero flow so that the Air Distribution
380 : // Unit(ADU) downstream knows that the system is operating or has flow. This is only done the first
381 : // iteration through and the splitter first pass. After the first iteration the ADU sets the
382 : // correct flow and that is used and passed back upstream.
383 20307326 : if (FirstHVACIteration && FirstCall) {
384 3970737 : if (state.dataLoopNodes->Node(InletNode).MassFlowRate > 0.0) {
385 13440975 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
386 10372398 : OutletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum);
387 10372398 : state.dataLoopNodes->Node(OutletNode).MassFlowRate =
388 10372398 : state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes;
389 : }
390 : }
391 3970737 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail > 0.0) {
392 13449901 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
393 10379106 : OutletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum);
394 10379106 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
395 20758212 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail /
396 10379106 : state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes;
397 : }
398 : }
399 :
400 : } // For FirstHVACIteration and FirstCall
401 :
402 20307326 : if (FirstCall) {
403 : // There is one exception to the rule stated above and that is if the system shuts OFF
404 : // for some operational or algorithm dependency. This IF block should catch that condition
405 : // and then pass the NO flow condition downstream to the waiting ADU's. Most of the time
406 : // this IF is jumped over.
407 10153663 : if (state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail == 0.0) {
408 :
409 7264244 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
410 :
411 5341612 : OutletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum);
412 5341612 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = 0.0;
413 5341612 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = 0.0;
414 5341612 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = 0.0;
415 : }
416 : } // For Node inlet Max Avail = 0.0
417 :
418 : // Pass the State Properties through every time. This is what mainly happens each time
419 : // through the splitter,
420 10153663 : InletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode;
421 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
422 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletHumRat = state.dataLoopNodes->Node(InletNode).HumRat;
423 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletEnthalpy = state.dataLoopNodes->Node(InletNode).Enthalpy;
424 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletPressure = state.dataLoopNodes->Node(InletNode).Press;
425 :
426 : } else { // On the second call from the ZoneEquipManager this is where the flows are passed back to
427 : // the splitter inlet.
428 42975168 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
429 :
430 32821505 : OutletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum);
431 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRate(NodeNum) =
432 32821505 : state.dataLoopNodes->Node(OutletNode).MassFlowRate;
433 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRateMaxAvail(NodeNum) =
434 32821505 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail;
435 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRateMinAvail(NodeNum) =
436 32821505 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail;
437 : }
438 :
439 : } // For FirstCall
440 20307326 : }
441 :
442 20307326 : void CalcAirLoopSplitter(EnergyPlusData &state, int const SplitterNum, bool const FirstCall)
443 : {
444 :
445 : // SUBROUTINE INFORMATION:
446 : // AUTHOR Richard J. Liesen
447 : // DATE WRITTEN March 2000
448 : // MODIFIED na
449 : // RE-ENGINEERED na
450 :
451 : // PURPOSE OF THIS SUBROUTINE:
452 : // This subroutine needs a description.
453 :
454 : // METHODOLOGY EMPLOYED:
455 : // Needs description, as appropriate.
456 :
457 : int OutletNodeNum;
458 :
459 : // The first time through the State properties are split and passed through
460 20307326 : if (FirstCall) {
461 : // Moisture balance to get outlet air humidity ratio
462 42975168 : for (OutletNodeNum = 1; OutletNodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutletNodeNum) {
463 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletHumRat(OutletNodeNum) =
464 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletHumRat;
465 : }
466 :
467 : // "Momentum balance" to get outlet air pressure
468 42975168 : for (OutletNodeNum = 1; OutletNodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutletNodeNum) {
469 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletPressure(OutletNodeNum) =
470 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletPressure;
471 : }
472 :
473 : // Energy balance to get outlet air enthalpy
474 42975168 : for (OutletNodeNum = 1; OutletNodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutletNodeNum) {
475 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletEnthalpy(OutletNodeNum) =
476 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletEnthalpy;
477 : }
478 :
479 : // Set outlet temperatures equal to inlet temperature
480 42975168 : for (OutletNodeNum = 1; OutletNodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutletNodeNum) {
481 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletTemp(OutletNodeNum) =
482 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletTemp;
483 : }
484 :
485 : } else {
486 : // This is the second time through and this is where the mass flows from each outlet are
487 : // summed and then assigned upstream to the inlet node.
488 : // Overall Mass Continuity Equation to get inlet mass flow rates
489 : // Zero the inlet Totals before the Inlets are summed
490 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRate = 0.0;
491 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMaxAvail = 0.0;
492 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMinAvail = 0.0;
493 :
494 42975168 : for (OutletNodeNum = 1; OutletNodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++OutletNodeNum) {
495 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRate +=
496 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRate(OutletNodeNum);
497 :
498 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMaxAvail +=
499 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRateMaxAvail(OutletNodeNum);
500 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMinAvail +=
501 32821505 : state.dataSplitterComponent->SplitterCond(SplitterNum).OutletMassFlowRateMinAvail(OutletNodeNum);
502 : }
503 :
504 : // What happens if Splitter inlet mass flow rate is greater than max available
505 : }
506 20307326 : }
507 :
508 : // End Algorithm Section of the Module
509 : // *****************************************************************************
510 :
511 : // Beginning of Update subroutines for the Splitter Module
512 : // *****************************************************************************
513 :
514 20307326 : void UpdateSplitter(EnergyPlusData &state, int const SplitterNum, bool &SplitterInletChanged, bool const FirstCall)
515 : {
516 : // SUBROUTINE INFORMATION:
517 : // AUTHOR Richard J. Liesen
518 : // DATE WRITTEN March 2000
519 : // MODIFIED na
520 : // RE-ENGINEERED na
521 :
522 20307326 : Real64 constexpr FlowRateToler(0.01); // Tolerance for mass flow rate convergence (in kg/s)
523 :
524 : int InletNode;
525 : int OutletNode;
526 : int NodeNum;
527 :
528 : // Set the inlet node for this splitter to be used throughout subroutine for either case
529 20307326 : InletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).InletNode;
530 :
531 : // On the FirstCall the State properties are passed through and the mass flows are not dealt with
532 : // except for NO flow conditions
533 20307326 : if (FirstCall) {
534 : // Set the outlet nodes for properties that just pass through & not used
535 42975168 : for (NodeNum = 1; NodeNum <= state.dataSplitterComponent->SplitterCond(SplitterNum).NumOutletNodes; ++NodeNum) {
536 32821505 : OutletNode = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletNode(NodeNum);
537 32821505 : state.dataLoopNodes->Node(OutletNode).Temp = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletTemp(NodeNum);
538 32821505 : state.dataLoopNodes->Node(OutletNode).HumRat = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletHumRat(NodeNum);
539 32821505 : state.dataLoopNodes->Node(OutletNode).Enthalpy = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletEnthalpy(NodeNum);
540 32821505 : state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(InletNode).Quality;
541 32821505 : state.dataLoopNodes->Node(OutletNode).Press = state.dataSplitterComponent->SplitterCond(SplitterNum).OutletPressure(NodeNum);
542 32821505 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
543 115568 : state.dataLoopNodes->Node(OutletNode).CO2 = state.dataLoopNodes->Node(InletNode).CO2;
544 : }
545 32821505 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
546 28848 : state.dataLoopNodes->Node(OutletNode).GenContam = state.dataLoopNodes->Node(InletNode).GenContam;
547 : }
548 : }
549 :
550 : } else {
551 : // The second time through just updates the mass flow conditions back upstream
552 : // to the inlet. Before it sets the inlet it checks to see that the flow rate has not
553 : // changed or not. The tolerance has been relaxed some now that the splitter has been
554 : // re-written
555 :
556 : // Set the outlet air nodes of the Splitter if the splitter results have changed
557 : // beyond the tolerance.
558 30460989 : if (std::abs(state.dataLoopNodes->Node(InletNode).MassFlowRate -
559 20307326 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRate) > FlowRateToler) {
560 1979457 : SplitterInletChanged = true;
561 : }
562 10153663 : state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRate;
563 10153663 : state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail =
564 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMaxAvail;
565 10153663 : state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail =
566 10153663 : state.dataSplitterComponent->SplitterCond(SplitterNum).InletMassFlowRateMinAvail;
567 :
568 : } // The FirstCall END IF
569 20307326 : }
570 :
571 20307326 : void ReportSplitter([[maybe_unused]] int const SplitterNum)
572 : {
573 :
574 : // SUBROUTINE INFORMATION:
575 : // AUTHOR Richard J. Liesen
576 : // DATE WRITTEN March 2000
577 : // MODIFIED na
578 : // RE-ENGINEERED na
579 :
580 : // Write(*,*)=SplitterCond(SplitterNum)%SplitterPower Still needs to report the Splitter power from this component
581 20307326 : }
582 :
583 24 : int GetSplitterOutletNumber(EnergyPlusData &state,
584 : std::string const &SplitterName, // must match Splitter names for the Splitter type
585 : int const SplitterNum, // Index of Splitters
586 : bool &ErrorsFound // set to true if problem
587 : )
588 : {
589 :
590 : // FUNCTION INFORMATION:
591 : // AUTHOR Lixing Gu
592 : // DATE WRITTEN Feb 2013
593 : // MODIFIED na
594 : // RE-ENGINEERED na
595 :
596 : // PURPOSE OF THIS FUNCTION:
597 : // This function looks up the given AirLoopHVAC:ZoneSplitter and returns the number of outlet nodes. If
598 : // incorrect AirLoopHVAC:ZoneSplitter name is given, ErrorsFound is returned as true
599 : // as zero.
600 :
601 : // Return value
602 : int SplitterOutletNumber;
603 :
604 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
605 : int WhichSplitter;
606 :
607 : // Obtains and Allocates AirLoopHVAC:ZoneSplitter related parameters from input file
608 24 : if (state.dataSplitterComponent->GetSplitterInputFlag) { // First time subroutine has been entered
609 0 : GetSplitterInput(state);
610 0 : state.dataSplitterComponent->GetSplitterInputFlag = false;
611 : }
612 :
613 24 : if (SplitterNum == 0) {
614 0 : WhichSplitter =
615 0 : UtilityRoutines::FindItemInList(SplitterName, state.dataSplitterComponent->SplitterCond, &SplitterConditions::SplitterName);
616 : } else {
617 24 : WhichSplitter = SplitterNum;
618 : }
619 :
620 24 : if (WhichSplitter != 0) {
621 24 : SplitterOutletNumber = state.dataSplitterComponent->SplitterCond(WhichSplitter).NumOutletNodes;
622 : }
623 :
624 24 : if (WhichSplitter == 0) {
625 0 : ShowSevereError(state, "GetSplitterOuletNumber: Could not find Splitter = \"" + SplitterName + "\"");
626 0 : ErrorsFound = true;
627 0 : SplitterOutletNumber = 0;
628 : }
629 :
630 24 : return SplitterOutletNumber;
631 : }
632 :
633 24 : Array1D_int GetSplitterNodeNumbers(EnergyPlusData &state,
634 : std::string const &SplitterName, // must match Splitter names for the Splitter type
635 : int const SplitterNum, // Index of Splitters
636 : bool &ErrorsFound // set to true if problem
637 : )
638 : {
639 :
640 : // FUNCTION INFORMATION:
641 : // AUTHOR Lixing Gu
642 : // DATE WRITTEN Feb 2013
643 : // MODIFIED na
644 : // RE-ENGINEERED na
645 :
646 : // PURPOSE OF THIS FUNCTION:
647 : // This function looks up the given AirLoopHVAC:ZoneSplitter and returns the node numbers. If
648 : // incorrect AirLoopHVAC:ZoneSplitter name is given, ErrorsFound is returned as true
649 : // as zero.
650 :
651 : // Return value
652 24 : Array1D_int SplitterNodeNumbers;
653 :
654 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
655 : int WhichSplitter;
656 : int i;
657 :
658 : // Obtains and Allocates AirLoopHVAC:ZoneSplitter related parameters from input file
659 24 : if (state.dataSplitterComponent->GetSplitterInputFlag) { // First time subroutine has been entered
660 0 : GetSplitterInput(state);
661 0 : state.dataSplitterComponent->GetSplitterInputFlag = false;
662 : }
663 :
664 24 : if (SplitterNum == 0) {
665 0 : WhichSplitter =
666 0 : UtilityRoutines::FindItemInList(SplitterName, state.dataSplitterComponent->SplitterCond, &SplitterConditions::SplitterName);
667 : } else {
668 24 : WhichSplitter = SplitterNum;
669 : }
670 :
671 24 : if (WhichSplitter != 0) {
672 24 : SplitterNodeNumbers.allocate(state.dataSplitterComponent->SplitterCond(WhichSplitter).NumOutletNodes + 2);
673 24 : SplitterNodeNumbers(1) = state.dataSplitterComponent->SplitterCond(WhichSplitter).InletNode;
674 24 : SplitterNodeNumbers(2) = state.dataSplitterComponent->SplitterCond(WhichSplitter).NumOutletNodes;
675 74 : for (i = 1; i <= SplitterNodeNumbers(2); ++i) {
676 50 : SplitterNodeNumbers(i + 2) = state.dataSplitterComponent->SplitterCond(WhichSplitter).OutletNode(i);
677 : }
678 : }
679 :
680 24 : if (WhichSplitter == 0) {
681 0 : ShowSevereError(state, "GetSplitterNodeNumbers: Could not find Splitter = \"" + SplitterName + "\"");
682 0 : ErrorsFound = true;
683 : }
684 :
685 24 : return SplitterNodeNumbers;
686 : }
687 :
688 : } // namespace SplitterComponent
689 :
690 2313 : } // namespace EnergyPlus
|