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