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 : #include <string>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/Autosizing/Base.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataContaminantBalance.hh>
61 : #include <EnergyPlus/DataConvergParams.hh>
62 : #include <EnergyPlus/DataDefineEquip.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHVACGlobals.hh>
65 : #include <EnergyPlus/DataHeatBalFanSys.hh>
66 : #include <EnergyPlus/DataHeatBalance.hh>
67 : #include <EnergyPlus/DataLoopNode.hh>
68 : #include <EnergyPlus/DataSizing.hh>
69 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
70 : #include <EnergyPlus/DataZoneEquipment.hh>
71 : #include <EnergyPlus/DualDuct.hh>
72 : #include <EnergyPlus/GeneralRoutines.hh>
73 : #include <EnergyPlus/GlobalNames.hh>
74 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
75 : #include <EnergyPlus/NodeInputManager.hh>
76 : #include <EnergyPlus/OutputProcessor.hh>
77 : #include <EnergyPlus/Psychrometrics.hh>
78 : #include <EnergyPlus/ScheduleManager.hh>
79 : #include <EnergyPlus/UtilityRoutines.hh>
80 :
81 : namespace EnergyPlus {
82 :
83 : namespace DualDuct {
84 : // Module containing the DualDuct simulation routines
85 :
86 : // MODULE INFORMATION:
87 : // AUTHOR Richard J. Liesen
88 : // DATE WRITTEN February 2000
89 : // MODIFIED Clayton Miller, Brent Griffith Aug. 2010 - Added DualDuctOA Terminal Unit to Simulate Decoupled OA/RA
90 : // RE-ENGINEERED na
91 :
92 : // PURPOSE OF THIS MODULE:
93 : // To encapsulate the data and algorithms required to
94 : // manage the DualDuct Systems Simulation
95 :
96 : constexpr std::string_view cCMO_DDConstantVolume = "AirTerminal:DualDuct:ConstantVolume";
97 : constexpr std::string_view cCMO_DDVariableVolume = "AirTerminal:DualDuct:VAV";
98 : constexpr std::string_view cCMO_DDVarVolOA = "AirTerminal:DualDuct:VAV:OutdoorAir";
99 : constexpr Real64 DualDuctMassFlowSetToler = DataConvergParams::HVACFlowRateToler * 0.00001;
100 : constexpr std::array<std::string_view, static_cast<int>(PerPersonMode::Num)> modeStrings = {"NOTSET", "CURRENTOCCUPANCY", "DESIGNOCCUPANCY"};
101 : constexpr std::array<std::string_view, static_cast<int>(DualDuctDamper::Num)> damperTypeStrings = {"ConstantVolume", "VAV", "VAV:OutdoorAir"};
102 : constexpr std::array<std::string_view, static_cast<int>(DualDuctDamper::Num)> cmoNameArray = {
103 : cCMO_DDConstantVolume, cCMO_DDVariableVolume, cCMO_DDVarVolOA};
104 :
105 206846 : void SimulateDualDuct(
106 : EnergyPlusData &state, std::string_view CompName, bool const FirstHVACIteration, int const ZoneNum, int const ZoneNodeNum, int &CompIndex)
107 : {
108 :
109 : // SUBROUTINE INFORMATION:
110 : // AUTHOR Richard Liesen
111 : // DATE WRITTEN February 2000
112 : // MODIFIED na
113 : // RE-ENGINEERED na
114 :
115 : // PURPOSE OF THIS SUBROUTINE:
116 : // This subroutine manages Damper component simulation.
117 : // It is called from the SimAirLoopComponent
118 : // at the system time step.
119 :
120 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
121 : int DDNum; // The Damper that you are currently loading input into
122 :
123 : // Obtains and Allocates Damper related parameters from input file
124 206846 : if (state.dataDualDuct->GetDualDuctInputFlag) { // First time subroutine has been entered
125 8 : GetDualDuctInput(state);
126 8 : state.dataDualDuct->GetDualDuctInputFlag = false;
127 : }
128 :
129 : // Find the correct DDNumber with the AirLoop & CompNum from AirLoop Derived Type
130 206846 : if (CompIndex == 0) {
131 34 : DDNum = UtilityRoutines::FindItemInList(CompName, state.dataDualDuct->dd_airterminal, &DualDuctAirTerminal::Name);
132 34 : if (DDNum == 0) {
133 0 : ShowFatalError(state, format("SimulateDualDuct: Damper not found={}", CompName));
134 : }
135 34 : CompIndex = DDNum;
136 : } else {
137 206812 : DDNum = CompIndex;
138 206812 : if (DDNum > state.dataDualDuct->NumDDAirTerminal || DDNum < 1) {
139 0 : ShowFatalError(state,
140 0 : format("SimulateDualDuct: Invalid CompIndex passed={}, Number of Dampers={}, Damper name={}",
141 : CompIndex,
142 0 : state.dataDualDuct->NumDDAirTerminal,
143 0 : CompName));
144 : }
145 206812 : if (state.dataDualDuct->dd_airterminal(DDNum).CheckEquipName) {
146 34 : if (CompName != state.dataDualDuct->dd_airterminal(DDNum).Name) {
147 0 : ShowFatalError(state,
148 0 : format("SimulateDualDuct: Invalid CompIndex passed={}, Damper name={}, stored Damper Name for that index={}",
149 : CompIndex,
150 : CompName,
151 0 : state.dataDualDuct->dd_airterminal(DDNum).Name));
152 : }
153 34 : state.dataDualDuct->dd_airterminal(DDNum).CheckEquipName = false;
154 : }
155 : }
156 :
157 206846 : auto &thisDualDuct(state.dataDualDuct->dd_airterminal(DDNum));
158 :
159 206846 : if (CompIndex > 0) {
160 206846 : state.dataSize->CurTermUnitSizingNum = state.dataDefineEquipment->AirDistUnit(thisDualDuct.ADUNum).TermUnitSizingNum;
161 : // With the correct DDNum Initialize
162 206846 : thisDualDuct.InitDualDuct(state, FirstHVACIteration); // Initialize all Damper related parameters
163 :
164 : // Calculate the Correct Damper Model with the current DDNum
165 206846 : switch (thisDualDuct.DamperType) {
166 122805 : case DualDuctDamper::ConstantVolume: { // 'AirTerminal:DualDuct:ConstantVolume'
167 122805 : thisDualDuct.SimDualDuctConstVol(state, ZoneNum, ZoneNodeNum);
168 122805 : } break;
169 16413 : case DualDuctDamper::VariableVolume: { // 'AirTerminal:DualDuct:VAV'
170 16413 : thisDualDuct.SimDualDuctVarVol(state, ZoneNum, ZoneNodeNum);
171 16413 : } break;
172 67628 : case DualDuctDamper::OutdoorAir: {
173 67628 : thisDualDuct.SimDualDuctVAVOutdoorAir(state, ZoneNum, ZoneNodeNum); // 'AirTerminal:DualDuct:VAV:OutdoorAir'
174 67628 : } break;
175 0 : default:
176 0 : break;
177 : }
178 :
179 : // Update the current Damper to the outlet nodes
180 206846 : thisDualDuct.UpdateDualDuct(state);
181 : } else {
182 0 : ShowFatalError(state, format("SimulateDualDuct: Damper not found={}", CompName));
183 : }
184 206846 : }
185 :
186 8 : void GetDualDuctInput(EnergyPlusData &state)
187 : {
188 :
189 : // SUBROUTINE INFORMATION:
190 : // AUTHOR Richard Liesen
191 : // DATE WRITTEN April 1998
192 : // MODIFIED Julien Marrec of EffiBEM, 2017-12-18
193 : // RE-ENGINEERED na
194 :
195 : // PURPOSE OF THIS SUBROUTINE:
196 : // This subroutine is the main routine to call other input routines and Get routines
197 :
198 : // METHODOLOGY EMPLOYED:
199 : // Uses the status flags to trigger events.
200 :
201 : // SUBROUTINE PARAMETER DEFINITIONS:
202 : static constexpr std::string_view RoutineName("GetDualDuctInput: "); // include trailing bla
203 :
204 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
205 : int NumAlphas;
206 : int NumNums;
207 : int IOStat;
208 16 : Array1D<Real64> NumArray(2, 0.0);
209 16 : Array1D_string AlphArray(7);
210 16 : Array1D_string cAlphaFields(7); // Alpha field names
211 16 : Array1D_string cNumericFields(2); // Numeric field names
212 16 : Array1D_bool lAlphaBlanks(7, true); // Logical array, alpha field input BLANK = .TRUE.
213 16 : Array1D_bool lNumericBlanks(2, true); // Logical array, numeric field input BLANK = .TRUE.
214 16 : std::string CurrentModuleObject; // for ease in getting objects
215 8 : bool ErrorsFound(false); // If errors detected in input
216 : int SupAirIn; // controlled zone supply air inlet index
217 : int ADUNum; // loop control to search Air Distribution Units
218 8 : Real64 DummyOAFlow(0.0);
219 :
220 8 : int NumDualDuctConstVolDampers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDConstantVolume);
221 8 : int NumDualDuctVarVolDampers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVariableVolume);
222 8 : state.dataDualDuct->NumDualDuctVarVolOA = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVarVolOA);
223 8 : state.dataDualDuct->NumDDAirTerminal = NumDualDuctConstVolDampers + NumDualDuctVarVolDampers + state.dataDualDuct->NumDualDuctVarVolOA;
224 8 : state.dataDualDuct->dd_airterminal.allocate(state.dataDualDuct->NumDDAirTerminal);
225 8 : state.dataDualDuct->UniqueDualDuctAirTerminalNames.reserve(state.dataDualDuct->NumDDAirTerminal);
226 :
227 8 : if (NumDualDuctConstVolDampers > 0) {
228 6 : CurrentModuleObject = cCMO_DDConstantVolume;
229 26 : for (int DamperIndex = 1; DamperIndex <= NumDualDuctConstVolDampers; ++DamperIndex) {
230 :
231 : // Load the info from the damper
232 :
233 20 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
234 : CurrentModuleObject,
235 : DamperIndex,
236 : AlphArray,
237 : NumAlphas,
238 : NumArray,
239 : NumNums,
240 : IOStat,
241 : lNumericBlanks,
242 : lAlphaBlanks,
243 : cAlphaFields,
244 : cNumericFields);
245 :
246 : // Anything below this line in this control block should use DDNum
247 20 : int DDNum = DamperIndex;
248 20 : auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
249 40 : GlobalNames::VerifyUniqueInterObjectName(
250 40 : state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
251 20 : thisDD.Name = AlphArray(1);
252 20 : thisDD.DamperType = DualDuctDamper::ConstantVolume;
253 20 : if (lAlphaBlanks(2)) {
254 5 : thisDD.SchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
255 : } else {
256 15 : thisDD.SchedPtr = ScheduleManager::GetScheduleIndex(state, AlphArray(2));
257 15 : if (thisDD.SchedPtr == 0) {
258 0 : ShowSevereError(state,
259 0 : format("{}, \"{}\" {} = {} not found.", CurrentModuleObject, thisDD.Name, cAlphaFields(2), AlphArray(2)));
260 0 : ErrorsFound = true;
261 : }
262 : }
263 40 : thisDD.OutletNodeNum = GetOnlySingleNode(state,
264 20 : AlphArray(3),
265 : ErrorsFound,
266 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
267 : thisDD.Name,
268 : DataLoopNode::NodeFluidType::Air,
269 : DataLoopNode::ConnectionType::Outlet,
270 : NodeInputManager::CompFluidStream::Primary,
271 : DataLoopNode::ObjectIsNotParent,
272 20 : cAlphaFields(3));
273 40 : thisDD.HotAirInletNodeNum = GetOnlySingleNode(state,
274 20 : AlphArray(4),
275 : ErrorsFound,
276 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
277 : thisDD.Name,
278 : DataLoopNode::NodeFluidType::Air,
279 : DataLoopNode::ConnectionType::Inlet,
280 : NodeInputManager::CompFluidStream::Primary,
281 : DataLoopNode::ObjectIsNotParent,
282 20 : cAlphaFields(4));
283 40 : thisDD.ColdAirInletNodeNum = GetOnlySingleNode(state,
284 20 : AlphArray(5),
285 : ErrorsFound,
286 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
287 : thisDD.Name,
288 : DataLoopNode::NodeFluidType::Air,
289 : DataLoopNode::ConnectionType::Inlet,
290 : NodeInputManager::CompFluidStream::Primary,
291 : DataLoopNode::ObjectIsNotParent,
292 20 : cAlphaFields(5));
293 :
294 20 : thisDD.MaxAirVolFlowRate = NumArray(1);
295 20 : thisDD.ZoneMinAirFracDes = 0.0;
296 :
297 : // Register component set data - one for heat and one for cool
298 20 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":HEAT", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
299 20 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":COOL", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
300 :
301 90 : for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
302 70 : if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
303 20 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.ColdAirInletNodeNum;
304 20 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.HotAirInletNodeNum;
305 20 : thisDD.ADUNum = ADUNum;
306 : }
307 : }
308 : // one assumes if there isn't one assigned, it's an error?
309 20 : if (thisDD.ADUNum == 0) {
310 0 : auto &thisObjType = damperTypeStrings[static_cast<int>(thisDD.DamperType)];
311 0 : ShowSevereError(
312 : state,
313 0 : format("{}No matching List:Zone:AirTerminal for AirTerminal:DualDuct = [{},{}].", RoutineName, thisObjType, thisDD.Name));
314 0 : ShowContinueError(state, "...should have outlet node=" + state.dataLoopNodes->NodeID(thisDD.OutletNodeNum));
315 0 : ErrorsFound = true;
316 : } else {
317 :
318 : // Fill the Zone Equipment data with the inlet node numbers of this unit.
319 95 : for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
320 75 : auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
321 75 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
322 140 : for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
323 70 : if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
324 20 : if (state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).OutNode > 0) {
325 0 : ShowSevereError(state, "Error in connecting a terminal unit to a zone");
326 0 : ShowContinueError(state, state.dataLoopNodes->NodeID(thisDD.OutletNodeNum) + " already connects to another zone");
327 0 : ShowContinueError(state, "Occurs for terminal unit " + CurrentModuleObject + " = " + thisDD.Name);
328 0 : ShowContinueError(state, "Check terminal unit node names for errors");
329 0 : ErrorsFound = true;
330 : } else {
331 20 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.ColdAirInletNodeNum;
332 20 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.HotAirInletNodeNum;
333 20 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
334 20 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
335 20 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
336 20 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
337 20 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
338 : }
339 20 : thisDD.CtrlZoneNum = CtrlZone;
340 20 : thisDD.CtrlZoneInNodeIndex = SupAirIn;
341 : }
342 : }
343 : }
344 : }
345 : // Setup the Average damper Position output variable
346 : // CurrentModuleObject='AirTerminal:DualDuct:ConstantVolume'
347 40 : SetupOutputVariable(state,
348 : "Zone Air Terminal Cold Supply Duct Damper Position",
349 : OutputProcessor::Unit::None,
350 : thisDD.ColdAirDamperPosition,
351 : OutputProcessor::SOVTimeStepType::System,
352 : OutputProcessor::SOVStoreType::Average,
353 20 : thisDD.Name);
354 40 : SetupOutputVariable(state,
355 : "Zone Air Terminal Hot Supply Duct Damper Position",
356 : OutputProcessor::Unit::None,
357 : thisDD.HotAirDamperPosition,
358 : OutputProcessor::SOVTimeStepType::System,
359 : OutputProcessor::SOVStoreType::Average,
360 20 : thisDD.Name);
361 :
362 : } // end Number of Damper Loop
363 : }
364 :
365 8 : if (NumDualDuctVarVolDampers > 0) {
366 1 : CurrentModuleObject = cCMO_DDVariableVolume;
367 4 : for (int DamperIndex = 1; DamperIndex <= NumDualDuctVarVolDampers; ++DamperIndex) {
368 :
369 : // Load the info from the damper
370 :
371 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
372 : CurrentModuleObject,
373 : DamperIndex,
374 : AlphArray,
375 : NumAlphas,
376 : NumArray,
377 : NumNums,
378 : IOStat,
379 : lNumericBlanks,
380 : lAlphaBlanks,
381 : cAlphaFields,
382 : cNumericFields);
383 :
384 : // Anything below this line in this control block should use DDNum
385 3 : int DDNum = DamperIndex + NumDualDuctConstVolDampers;
386 3 : auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
387 6 : GlobalNames::VerifyUniqueInterObjectName(
388 6 : state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
389 3 : thisDD.Name = AlphArray(1);
390 3 : thisDD.DamperType = DualDuctDamper::VariableVolume;
391 3 : if (lAlphaBlanks(2)) {
392 0 : thisDD.SchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
393 : } else {
394 3 : thisDD.SchedPtr = ScheduleManager::GetScheduleIndex(state, AlphArray(2));
395 3 : if (thisDD.SchedPtr == 0) {
396 0 : ShowSevereError(state,
397 0 : format("{}, \"{}\" {} = {} not found.", CurrentModuleObject, thisDD.Name, cAlphaFields(2), AlphArray(2)));
398 0 : ErrorsFound = true;
399 : }
400 : }
401 6 : thisDD.OutletNodeNum = GetOnlySingleNode(state,
402 3 : AlphArray(3),
403 : ErrorsFound,
404 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
405 : thisDD.Name,
406 : DataLoopNode::NodeFluidType::Air,
407 : DataLoopNode::ConnectionType::Outlet,
408 : NodeInputManager::CompFluidStream::Primary,
409 : DataLoopNode::ObjectIsNotParent,
410 3 : cAlphaFields(3));
411 6 : thisDD.HotAirInletNodeNum = GetOnlySingleNode(state,
412 3 : AlphArray(4),
413 : ErrorsFound,
414 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
415 : thisDD.Name,
416 : DataLoopNode::NodeFluidType::Air,
417 : DataLoopNode::ConnectionType::Inlet,
418 : NodeInputManager::CompFluidStream::Primary,
419 : DataLoopNode::ObjectIsNotParent,
420 3 : cAlphaFields(4));
421 6 : thisDD.ColdAirInletNodeNum = GetOnlySingleNode(state,
422 3 : AlphArray(5),
423 : ErrorsFound,
424 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
425 : thisDD.Name,
426 : DataLoopNode::NodeFluidType::Air,
427 : DataLoopNode::ConnectionType::Inlet,
428 : NodeInputManager::CompFluidStream::Primary,
429 : DataLoopNode::ObjectIsNotParent,
430 3 : cAlphaFields(5));
431 :
432 3 : thisDD.MaxAirVolFlowRate = NumArray(1);
433 3 : thisDD.ZoneMinAirFracDes = NumArray(2);
434 :
435 : // Register component set data - one for heat and one for cool
436 3 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":HEAT", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
437 3 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":COOL", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
438 :
439 12 : for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
440 9 : if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
441 3 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.ColdAirInletNodeNum;
442 3 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.HotAirInletNodeNum;
443 3 : thisDD.ADUNum = ADUNum;
444 : }
445 : }
446 : // one assumes if there isn't one assigned, it's an error?
447 3 : if (thisDD.ADUNum == 0) {
448 0 : auto &thisObjType = damperTypeStrings[static_cast<int>(thisDD.DamperType)];
449 0 : ShowSevereError(
450 : state,
451 0 : format("{}No matching List:Zone:AirTerminal for AirTerminal:DualDuct = [{},{}].", RoutineName, thisObjType, thisDD.Name));
452 0 : ShowContinueError(state, format("...should have outlet node={}", state.dataLoopNodes->NodeID(thisDD.OutletNodeNum)));
453 0 : ErrorsFound = true;
454 : } else {
455 :
456 : // Fill the Zone Equipment data with the inlet node numbers of this unit.
457 12 : for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
458 9 : auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
459 9 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
460 18 : for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
461 9 : if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
462 3 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.ColdAirInletNodeNum;
463 3 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.HotAirInletNodeNum;
464 3 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
465 3 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
466 3 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
467 3 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
468 3 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
469 :
470 3 : thisDD.CtrlZoneNum = CtrlZone;
471 3 : thisDD.CtrlZoneInNodeIndex = SupAirIn;
472 : }
473 : }
474 : }
475 : }
476 3 : if (!lAlphaBlanks(6)) {
477 0 : thisDD.OARequirementsPtr = UtilityRoutines::FindItemInList(AlphArray(6), state.dataSize->OARequirements);
478 0 : if (thisDD.OARequirementsPtr == 0) {
479 0 : ShowSevereError(state, cAlphaFields(6) + " = " + AlphArray(6) + " not found.");
480 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVariableVolume, thisDD.Name));
481 0 : ErrorsFound = true;
482 : } else {
483 0 : thisDD.NoOAFlowInputFromUser = false;
484 : }
485 : }
486 :
487 3 : if (lAlphaBlanks(7)) {
488 3 : thisDD.ZoneTurndownMinAirFrac = 1.0;
489 3 : thisDD.ZoneTurndownMinAirFracSchExist = false;
490 : } else {
491 0 : thisDD.ZoneTurndownMinAirFracSchPtr = ScheduleManager::GetScheduleIndex(state, AlphArray(7));
492 0 : if (thisDD.ZoneTurndownMinAirFracSchPtr == 0) {
493 0 : ShowSevereError(state, format("{} = {} not found.", cAlphaFields(7), AlphArray(7)));
494 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVariableVolume, thisDD.Name));
495 0 : ErrorsFound = true;
496 : }
497 0 : thisDD.ZoneTurndownMinAirFracSchExist = true;
498 : }
499 :
500 : // Setup the Average damper Position output variable
501 : // CurrentModuleObject='AirTerminal:DualDuct:VAV'
502 6 : SetupOutputVariable(state,
503 : "Zone Air Terminal Cold Supply Duct Damper Position",
504 : OutputProcessor::Unit::None,
505 : thisDD.ColdAirDamperPosition,
506 : OutputProcessor::SOVTimeStepType::System,
507 : OutputProcessor::SOVStoreType::Average,
508 3 : thisDD.Name);
509 6 : SetupOutputVariable(state,
510 : "Zone Air Terminal Hot Supply Duct Damper Position",
511 : OutputProcessor::Unit::None,
512 : thisDD.HotAirDamperPosition,
513 : OutputProcessor::SOVTimeStepType::System,
514 : OutputProcessor::SOVStoreType::Average,
515 3 : thisDD.Name);
516 6 : SetupOutputVariable(state,
517 : "Zone Air Terminal Outdoor Air Volume Flow Rate",
518 : OutputProcessor::Unit::m3_s,
519 : thisDD.OutdoorAirFlowRate,
520 : OutputProcessor::SOVTimeStepType::System,
521 : OutputProcessor::SOVStoreType::Average,
522 3 : thisDD.Name);
523 : } // end Number of Damper Loop
524 : }
525 :
526 8 : if (state.dataDualDuct->NumDualDuctVarVolOA > 0) {
527 1 : CurrentModuleObject = cCMO_DDVarVolOA;
528 12 : for (int DamperIndex = 1; DamperIndex <= state.dataDualDuct->NumDualDuctVarVolOA; ++DamperIndex) {
529 :
530 : // Load the info from the damper
531 11 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
532 : CurrentModuleObject,
533 : DamperIndex,
534 : AlphArray,
535 : NumAlphas,
536 : NumArray,
537 : NumNums,
538 : IOStat,
539 : lNumericBlanks,
540 : lAlphaBlanks,
541 : cAlphaFields,
542 : cNumericFields);
543 :
544 : // Anything below this line in this control block should use DDNum
545 11 : int DDNum = DamperIndex + NumDualDuctConstVolDampers + NumDualDuctVarVolDampers;
546 11 : auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
547 22 : GlobalNames::VerifyUniqueInterObjectName(
548 22 : state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
549 11 : thisDD.Name = AlphArray(1);
550 11 : thisDD.DamperType = DualDuctDamper::OutdoorAir;
551 11 : if (lAlphaBlanks(2)) {
552 0 : thisDD.SchedPtr = DataGlobalConstants::ScheduleAlwaysOn;
553 : } else {
554 11 : thisDD.SchedPtr = ScheduleManager::GetScheduleIndex(state, AlphArray(2));
555 11 : if (thisDD.SchedPtr == 0) {
556 0 : ShowSevereError(state,
557 0 : format("{}, \"{}\" {} = {} not found.", CurrentModuleObject, thisDD.Name, cAlphaFields(2), AlphArray(2)));
558 0 : ErrorsFound = true;
559 : }
560 : }
561 22 : thisDD.OutletNodeNum = GetOnlySingleNode(state,
562 11 : AlphArray(3),
563 : ErrorsFound,
564 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
565 : thisDD.Name,
566 : DataLoopNode::NodeFluidType::Air,
567 : DataLoopNode::ConnectionType::Outlet,
568 : NodeInputManager::CompFluidStream::Primary,
569 : DataLoopNode::ObjectIsNotParent,
570 11 : cAlphaFields(3));
571 22 : thisDD.OAInletNodeNum = GetOnlySingleNode(state,
572 11 : AlphArray(4),
573 : ErrorsFound,
574 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
575 : thisDD.Name,
576 : DataLoopNode::NodeFluidType::Air,
577 : DataLoopNode::ConnectionType::Inlet,
578 : NodeInputManager::CompFluidStream::Primary,
579 : DataLoopNode::ObjectIsNotParent,
580 11 : cAlphaFields(4));
581 :
582 11 : if (!lAlphaBlanks(5)) {
583 12 : thisDD.RecircAirInletNodeNum = GetOnlySingleNode(state,
584 6 : AlphArray(5),
585 : ErrorsFound,
586 : DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
587 : thisDD.Name,
588 : DataLoopNode::NodeFluidType::Air,
589 : DataLoopNode::ConnectionType::Inlet,
590 : NodeInputManager::CompFluidStream::Primary,
591 : DataLoopNode::ObjectIsNotParent,
592 6 : cAlphaFields(5));
593 : } else {
594 : // for this model, we intentionally allow not using the recirc side
595 5 : thisDD.RecircIsUsed = false;
596 : }
597 :
598 11 : thisDD.MaxAirVolFlowRate = NumArray(1);
599 11 : thisDD.MaxAirMassFlowRate = thisDD.MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
600 :
601 : // Register component set data - one for OA and one for RA
602 11 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":OutdoorAir", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
603 11 : if (thisDD.RecircIsUsed) {
604 24 : BranchNodeConnections::TestCompSet(
605 18 : state, CurrentModuleObject + ":RecirculatedAir", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
606 : }
607 :
608 11 : thisDD.OAPerPersonMode = static_cast<PerPersonMode>(getEnumerationValue(modeStrings, AlphArray(7)));
609 11 : if (thisDD.OAPerPersonMode == PerPersonMode::Invalid) {
610 4 : thisDD.OAPerPersonMode = PerPersonMode::ModeNotSet;
611 : }
612 : // checks on this are done later
613 :
614 132 : for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
615 121 : if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
616 11 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.OAInletNodeNum;
617 11 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.RecircAirInletNodeNum;
618 11 : thisDD.ADUNum = ADUNum;
619 : }
620 : }
621 : // one assumes if there isn't one assigned, it's an error?
622 11 : if (thisDD.ADUNum == 0) {
623 0 : auto &thisObjType = damperTypeStrings[static_cast<int>(thisDD.DamperType)];
624 0 : ShowSevereError(
625 : state,
626 0 : format("{}No matching List:Zone:AirTerminal for AirTerminal:DualDuct = [{},{}].", RoutineName, thisObjType, thisDD.Name));
627 0 : ShowContinueError(state, format("...should have outlet node={}", state.dataLoopNodes->NodeID(thisDD.OutletNodeNum)));
628 0 : ErrorsFound = true;
629 : } else {
630 :
631 : // Fill the Zone Equipment data with the inlet node numbers of this unit.
632 132 : for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
633 121 : auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
634 121 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
635 297 : for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
636 176 : if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
637 11 : if (thisDD.RecircIsUsed) {
638 6 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.RecircAirInletNodeNum;
639 : } else {
640 5 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.OAInletNodeNum;
641 : }
642 11 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.OAInletNodeNum;
643 11 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
644 11 : thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
645 11 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
646 11 : thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
647 11 : state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
648 :
649 11 : thisDD.CtrlZoneNum = CtrlZone;
650 11 : thisDD.CtrlZoneInNodeIndex = SupAirIn;
651 : }
652 : }
653 : }
654 : }
655 11 : thisDD.OARequirementsPtr = UtilityRoutines::FindItemInList(AlphArray(6), state.dataSize->OARequirements);
656 11 : if (thisDD.OARequirementsPtr == 0) {
657 0 : ShowSevereError(state, format("{} = {} not found.", cAlphaFields(6), AlphArray(6)));
658 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVarVolOA, thisDD.Name));
659 0 : ErrorsFound = true;
660 : } else {
661 11 : thisDD.NoOAFlowInputFromUser = false;
662 :
663 : // now fill design OA rate
664 11 : thisDD.CalcOAOnlyMassFlow(state, DummyOAFlow, thisDD.DesignOAFlowRate);
665 :
666 11 : if (thisDD.MaxAirVolFlowRate != DataSizing::AutoSize) {
667 0 : BaseSizer::reportSizerOutput(
668 0 : state, CurrentModuleObject, thisDD.Name, "Maximum Outdoor Air Flow Rate [m3/s]", thisDD.DesignOAFlowRate);
669 :
670 0 : if (thisDD.RecircIsUsed) {
671 0 : thisDD.DesignRecircFlowRate = thisDD.MaxAirVolFlowRate - thisDD.DesignOAFlowRate;
672 0 : thisDD.DesignRecircFlowRate = max(0.0, thisDD.DesignRecircFlowRate);
673 0 : BaseSizer::reportSizerOutput(
674 0 : state, CurrentModuleObject, thisDD.Name, "Maximum Recirculated Air Flow Rate [m3/s]", thisDD.DesignRecircFlowRate);
675 : } else {
676 0 : if (thisDD.MaxAirVolFlowRate < thisDD.DesignOAFlowRate) {
677 0 : ShowSevereError(state,
678 0 : format("The value {:.5R} in {}is lower than the outdoor air requirement.",
679 : thisDD.MaxAirVolFlowRate,
680 0 : cNumericFields(1)));
681 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVarVolOA, thisDD.Name));
682 0 : ShowContinueError(state, format("The design outdoor air requirement is {:.5R}", thisDD.DesignOAFlowRate));
683 0 : ErrorsFound = true;
684 : }
685 : }
686 : }
687 : }
688 :
689 11 : if (thisDD.OAPerPersonMode == PerPersonMode::ModeNotSet) {
690 4 : DummyOAFlow = state.dataSize->OARequirements(thisDD.OARequirementsPtr).OAFlowPerPerson;
691 4 : if ((DummyOAFlow == 0.0) && (lAlphaBlanks(7))) { // no worries
692 : // do nothing, okay since no per person requirement involved
693 0 : } else if ((DummyOAFlow > 0.0) && (lAlphaBlanks(7))) { // missing input
694 0 : ShowSevereError(state, cAlphaFields(7) + " was blank.");
695 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVarVolOA, thisDD.Name));
696 0 : ShowContinueError(state, R"(Valid choices are "CurrentOccupancy" or "DesignOccupancy")");
697 0 : ErrorsFound = true;
698 0 : } else if ((DummyOAFlow > 0.0) && !(lAlphaBlanks(7))) { // incorrect input
699 0 : ShowSevereError(state, cAlphaFields(7) + " = " + AlphArray(7) + " not a valid key choice.");
700 0 : ShowContinueError(state, format("Occurs in {} = {}", cCMO_DDVarVolOA, thisDD.Name));
701 0 : ShowContinueError(state, R"(Valid choices are "CurrentOccupancy" or "DesignOccupancy")");
702 0 : ErrorsFound = true;
703 : }
704 : }
705 :
706 : // Setup the Average damper Position output variable
707 22 : SetupOutputVariable(state,
708 : "Zone Air Terminal Outdoor Air Duct Damper Position",
709 : OutputProcessor::Unit::None,
710 : thisDD.OADamperPosition,
711 : OutputProcessor::SOVTimeStepType::System,
712 : OutputProcessor::SOVStoreType::Average,
713 11 : thisDD.Name);
714 22 : SetupOutputVariable(state,
715 : "Zone Air Terminal Recirculated Air Duct Damper Position",
716 : OutputProcessor::Unit::None,
717 : thisDD.RecircAirDamperPosition,
718 : OutputProcessor::SOVTimeStepType::System,
719 : OutputProcessor::SOVStoreType::Average,
720 11 : thisDD.Name);
721 22 : SetupOutputVariable(state,
722 : "Zone Air Terminal Outdoor Air Fraction",
723 : OutputProcessor::Unit::None,
724 : thisDD.OAFraction,
725 : OutputProcessor::SOVTimeStepType::System,
726 : OutputProcessor::SOVStoreType::Average,
727 11 : thisDD.Name);
728 :
729 : } // end Number of Damper Loop
730 : }
731 :
732 8 : if (ErrorsFound) {
733 0 : ShowFatalError(state, std::string{RoutineName} + "Errors found in input. Preceding condition(s) cause termination.");
734 : }
735 8 : }
736 :
737 206846 : void DualDuctAirTerminal::InitDualDuct(EnergyPlusData &state, bool const FirstHVACIteration)
738 : {
739 :
740 : // SUBROUTINE INFORMATION:
741 : // AUTHOR Richard J. Liesen
742 : // DATE WRITTEN February 1998
743 : // MODIFIED na
744 : // RE-ENGINEERED na
745 :
746 : // PURPOSE OF THIS SUBROUTINE:
747 : // This subroutine is for initializations of the Damper Components.
748 :
749 : // METHODOLOGY EMPLOYED:
750 : // Uses the status flags to trigger events.
751 :
752 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
753 : int HotInNode;
754 : int ColdInNode;
755 : int OAInNode; // Outdoor Air Inlet Node for VAV:OutdoorAir units
756 : int RAInNode; // Reciruclated Air Inlet Node for VAV:OutdoorAir units
757 : int OutNode;
758 : int Loop; // Loop checking control variable
759 : Real64 PeopleFlow; // local sum variable, m3/s
760 :
761 206846 : if (!state.dataDualDuct->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
762 8 : state.dataDualDuct->ZoneEquipmentListChecked = true;
763 : // Check to see if there is a Air Distribution Unit on the Zone Equipment List
764 42 : for (Loop = 1; Loop <= state.dataDualDuct->NumDDAirTerminal; ++Loop) {
765 34 : if (this->ADUNum == 0) continue;
766 102 : if (DataZoneEquipment::CheckZoneEquipmentList(
767 68 : state, "ZONEHVAC:AIRDISTRIBUTIONUNIT", state.dataDefineEquipment->AirDistUnit(this->ADUNum).Name))
768 34 : continue;
769 0 : ShowSevereError(state,
770 0 : "InitDualDuct: ADU=[Air Distribution Unit," + state.dataDefineEquipment->AirDistUnit(this->ADUNum).Name +
771 : "] is not on any ZoneHVAC:EquipmentList.");
772 0 : if (this->DamperType == DualDuctDamper::ConstantVolume) {
773 0 : ShowContinueError(state, format("...Dual Duct Damper=[{},{}] will not be simulated.", cCMO_DDConstantVolume, this->Name));
774 0 : } else if (this->DamperType == DualDuctDamper::VariableVolume) {
775 0 : ShowContinueError(state, format("...Dual Duct Damper=[{},{}] will not be simulated.", cCMO_DDVariableVolume, this->Name));
776 0 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
777 0 : ShowContinueError(state, format("...Dual Duct Damper=[{},{}] will not be simulated.", cCMO_DDVarVolOA, this->Name));
778 : } else {
779 0 : ShowContinueError(state, "...Dual Duct Damper=[unknown/invalid," + this->Name + "] will not be simulated.");
780 : }
781 : }
782 : }
783 :
784 206846 : if (!state.dataGlobal->SysSizingCalc && this->MySizeFlag) {
785 34 : this->SizeDualDuct(state);
786 34 : this->MySizeFlag = false;
787 : }
788 :
789 : // Do the Begin Environment initializations
790 206846 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag) {
791 :
792 238 : if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
793 150 : OutNode = this->OutletNodeNum;
794 150 : HotInNode = this->HotAirInletNodeNum;
795 150 : ColdInNode = this->ColdAirInletNodeNum;
796 150 : state.dataLoopNodes->Node(OutNode).MassFlowRateMax = this->MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
797 150 : if (this->DamperType == DualDuctDamper::ConstantVolume) {
798 132 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
799 18 : } else if (this->DamperType == DualDuctDamper::VariableVolume) {
800 : // get dual duct air terminal box minimum flow fraction value
801 18 : if (this->ZoneTurndownMinAirFracSchExist) {
802 0 : this->ZoneTurndownMinAirFrac = ScheduleManager::GetScheduleMinValue(state, this->ZoneTurndownMinAirFracSchPtr);
803 : } else {
804 18 : this->ZoneTurndownMinAirFrac = 1.0;
805 : }
806 18 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin =
807 18 : state.dataLoopNodes->Node(OutNode).MassFlowRateMax * this->ZoneMinAirFracDes * this->ZoneTurndownMinAirFrac;
808 : } else {
809 0 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
810 : }
811 150 : this->dd_airterminalHotAirInlet.AirMassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
812 150 : this->dd_airterminalColdAirInlet.AirMassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
813 150 : state.dataLoopNodes->Node(HotInNode).MassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
814 150 : state.dataLoopNodes->Node(ColdInNode).MassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
815 150 : state.dataLoopNodes->Node(HotInNode).MassFlowRateMin = 0.0;
816 150 : state.dataLoopNodes->Node(ColdInNode).MassFlowRateMin = 0.0;
817 150 : this->MyEnvrnFlag = false;
818 :
819 88 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
820 : // Initialize for DualDuct:VAV:OutdoorAir
821 88 : OutNode = this->OutletNodeNum;
822 88 : OAInNode = this->OAInletNodeNum;
823 88 : if (this->RecircIsUsed) RAInNode = this->RecircAirInletNodeNum;
824 88 : state.dataLoopNodes->Node(OutNode).MassFlowRateMax = this->MaxAirMassFlowRate;
825 88 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
826 88 : this->dd_airterminalOAInlet.AirMassFlowRateMax = this->DesignOAFlowRate * state.dataEnvrn->StdRhoAir;
827 88 : if (this->RecircIsUsed) {
828 48 : this->dd_airterminalRecircAirInlet.AirMassFlowRateMax = this->MaxAirMassFlowRate - this->dd_airterminalOAInlet.AirMassFlowRateMax;
829 48 : state.dataLoopNodes->Node(RAInNode).MassFlowRateMax = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
830 48 : state.dataLoopNodes->Node(RAInNode).MassFlowRateMin = 0.0;
831 48 : this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag = 1.0e-10 * this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
832 : }
833 88 : state.dataLoopNodes->Node(OAInNode).MassFlowRateMax = this->dd_airterminalOAInlet.AirMassFlowRateMax;
834 88 : state.dataLoopNodes->Node(OAInNode).MassFlowRateMin = 0.0;
835 : // figure per person by design level for the OA duct.
836 88 : PeopleFlow = 0.0;
837 616 : for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
838 528 : if (state.dataHeatBal->People(Loop).ZonePtr != this->CtrlZoneNum) continue;
839 48 : DataSizing::OAFlowCalcMethod damperOAFlowMethod = state.dataSize->OARequirements(this->OARequirementsPtr).OAFlowMethod;
840 48 : if (damperOAFlowMethod == DataSizing::OAFlowCalcMethod::PerPerson || damperOAFlowMethod == DataSizing::OAFlowCalcMethod::Sum ||
841 : damperOAFlowMethod == DataSizing::OAFlowCalcMethod::Max) {
842 48 : PeopleFlow +=
843 48 : state.dataHeatBal->People(Loop).NumberOfPeople * state.dataSize->OARequirements(this->OARequirementsPtr).OAFlowPerPerson;
844 : }
845 : }
846 88 : this->MyEnvrnFlag = false;
847 : }
848 : }
849 :
850 206846 : if (!state.dataGlobal->BeginEnvrnFlag) {
851 204966 : this->MyEnvrnFlag = true;
852 : }
853 :
854 : // Find air loop associated with this terminal unit
855 206846 : if (this->MyAirLoopFlag) {
856 102 : if (this->AirLoopNum == 0) {
857 68 : if ((this->CtrlZoneNum > 0) && (this->CtrlZoneInNodeIndex > 0)) {
858 68 : this->AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->CtrlZoneNum).InletNodeAirLoopNum(this->CtrlZoneInNodeIndex);
859 68 : state.dataDefineEquipment->AirDistUnit(this->ADUNum).AirLoopNum = this->AirLoopNum;
860 : // Don't set MyAirLoopFlag to false yet because airloopnums might not be populated yet
861 : }
862 : } else {
863 34 : this->MyAirLoopFlag = false;
864 : }
865 : }
866 :
867 : // Initialize the Inlet Nodes of the Sys
868 206846 : if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
869 139218 : HotInNode = this->HotAirInletNodeNum;
870 139218 : ColdInNode = this->ColdAirInletNodeNum;
871 139218 : OutNode = this->OutletNodeNum;
872 67628 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
873 67628 : OAInNode = this->OAInletNodeNum;
874 67628 : if (this->RecircIsUsed) RAInNode = this->RecircAirInletNodeNum;
875 67628 : OutNode = this->OutletNodeNum;
876 : }
877 :
878 206846 : if (FirstHVACIteration) {
879 : // CALL DisplayString('Init First HVAC Iteration {'//TRIM( dd_airterminal(DDNum)%DamperName)//'}') !-For debugging - REMOVE
880 : // The first time through set the mass flow rate to the Max
881 : // Take care of the flow rates first. For Const Vol and VAV.
882 98800 : if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
883 65063 : auto &thisHotInNode = state.dataLoopNodes->Node(HotInNode);
884 65063 : auto &thisColdInNode = state.dataLoopNodes->Node(ColdInNode);
885 65063 : Real64 schedValue = ScheduleManager::GetCurrentScheduleValue(state, this->SchedPtr);
886 65063 : if ((thisHotInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
887 49712 : thisHotInNode.MassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
888 : } else {
889 15351 : thisHotInNode.MassFlowRate = 0.0;
890 : }
891 65063 : if ((thisColdInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
892 49707 : thisColdInNode.MassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
893 : } else {
894 15356 : thisColdInNode.MassFlowRate = 0.0;
895 : }
896 : // Next take care of the Max Avail Flow Rates
897 65063 : if ((thisHotInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
898 49712 : thisHotInNode.MassFlowRateMaxAvail = this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
899 : } else {
900 15351 : thisHotInNode.MassFlowRateMaxAvail = 0.0;
901 : }
902 65063 : if ((thisColdInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
903 49707 : thisColdInNode.MassFlowRateMaxAvail = this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
904 : } else {
905 15356 : thisColdInNode.MassFlowRateMaxAvail = 0.0;
906 : }
907 : // get current time step air terminal box turndown minimum flow fraction
908 65063 : if (this->ZoneTurndownMinAirFracSchExist) {
909 0 : this->ZoneTurndownMinAirFrac = ScheduleManager::GetCurrentScheduleValue(state, this->ZoneTurndownMinAirFracSchPtr);
910 : } else {
911 65063 : this->ZoneTurndownMinAirFrac = 1.0;
912 : }
913 : // update to the current dual duct minimum air flow fraction
914 65063 : this->ZoneMinAirFrac = this->ZoneMinAirFracDes * this->ZoneTurndownMinAirFrac;
915 : // The last item is to take care of the Min Avail Flow Rates
916 65063 : if ((thisHotInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
917 49712 : thisHotInNode.MassFlowRateMinAvail = this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
918 : } else {
919 15351 : thisHotInNode.MassFlowRateMinAvail = 0.0;
920 : }
921 65063 : if ((thisColdInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
922 49707 : thisColdInNode.MassFlowRateMinAvail = this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
923 : } else {
924 15356 : thisColdInNode.MassFlowRateMinAvail = 0.0;
925 65063 : }
926 :
927 33737 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
928 33737 : auto &thisOAInNode = state.dataLoopNodes->Node(OAInNode);
929 33737 : Real64 schedValue = ScheduleManager::GetCurrentScheduleValue(state, this->SchedPtr);
930 : // The first time through set the mass flow rate to the Max for VAV:OutdoorAir
931 33737 : if ((thisOAInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
932 33630 : thisOAInNode.MassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRateMax;
933 : } else {
934 107 : thisOAInNode.MassFlowRate = 0.0;
935 : }
936 33737 : if (this->RecircIsUsed) {
937 18402 : auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
938 18402 : if ((thisRAInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
939 18354 : thisRAInNode.MassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
940 : } else {
941 48 : thisRAInNode.MassFlowRate = 0.0;
942 : }
943 : // clear flow history
944 18402 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1 = 0.0;
945 18402 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2 = 0.0;
946 18402 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3 = 0.0;
947 : }
948 : // Next take care of the Max Avail Flow Rates
949 33737 : if ((thisOAInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
950 33630 : thisOAInNode.MassFlowRateMaxAvail = this->dd_airterminalOAInlet.AirMassFlowRateMax;
951 : } else {
952 107 : thisOAInNode.MassFlowRateMaxAvail = 0.0;
953 : }
954 33737 : if (this->RecircIsUsed) {
955 18402 : auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
956 18402 : if ((thisRAInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
957 18354 : thisRAInNode.MassFlowRateMaxAvail = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
958 : } else {
959 48 : thisRAInNode.MassFlowRateMaxAvail = 0.0;
960 : }
961 : }
962 : // The last item is to take care of the Min Avail Flow Rates. VAV:OutdoorAir
963 33737 : thisOAInNode.MassFlowRateMinAvail = 0.0;
964 33737 : if (this->RecircIsUsed) {
965 18402 : auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
966 18402 : thisRAInNode.MassFlowRateMinAvail = 0.0;
967 : }
968 : }
969 : }
970 :
971 : // Initialize the Inlet Nodes of the Dampers for Const. Vol and VAV
972 206846 : if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
973 :
974 139218 : this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail =
975 139218 : min(state.dataLoopNodes->Node(OutNode).MassFlowRateMax, state.dataLoopNodes->Node(HotInNode).MassFlowRateMaxAvail);
976 139218 : this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail =
977 139218 : min(max(state.dataLoopNodes->Node(OutNode).MassFlowRateMin, state.dataLoopNodes->Node(HotInNode).MassFlowRateMinAvail),
978 139218 : state.dataLoopNodes->Node(HotInNode).MassFlowRateMaxAvail);
979 :
980 139218 : this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail =
981 139218 : min(state.dataLoopNodes->Node(OutNode).MassFlowRateMax, state.dataLoopNodes->Node(ColdInNode).MassFlowRateMaxAvail);
982 139218 : this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail =
983 139218 : min(max(state.dataLoopNodes->Node(OutNode).MassFlowRateMin, state.dataLoopNodes->Node(ColdInNode).MassFlowRateMinAvail),
984 139218 : state.dataLoopNodes->Node(ColdInNode).MassFlowRateMaxAvail);
985 :
986 : // Do the following initializations (every time step): This should be the info from
987 : // the previous components outlets or the node data in this section.
988 : // Load the node data in this section for the component simulation
989 139218 : this->dd_airterminalHotAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(HotInNode).MassFlowRate;
990 139218 : this->dd_airterminalHotAirInlet.AirTemp = state.dataLoopNodes->Node(HotInNode).Temp;
991 139218 : this->dd_airterminalHotAirInlet.AirHumRat = state.dataLoopNodes->Node(HotInNode).HumRat;
992 139218 : this->dd_airterminalHotAirInlet.AirEnthalpy = state.dataLoopNodes->Node(HotInNode).Enthalpy;
993 139218 : this->dd_airterminalColdAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(ColdInNode).MassFlowRate;
994 139218 : this->dd_airterminalColdAirInlet.AirTemp = state.dataLoopNodes->Node(ColdInNode).Temp;
995 139218 : this->dd_airterminalColdAirInlet.AirHumRat = state.dataLoopNodes->Node(ColdInNode).HumRat;
996 139218 : this->dd_airterminalColdAirInlet.AirEnthalpy = state.dataLoopNodes->Node(ColdInNode).Enthalpy;
997 :
998 : // Initialize the Inlet Nodes of the Dampers for VAV:OutdoorAir
999 67628 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
1000 67628 : this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail = state.dataLoopNodes->Node(OAInNode).MassFlowRateMaxAvail;
1001 67628 : this->dd_airterminalOAInlet.AirMassFlowRateMinAvail = state.dataLoopNodes->Node(OAInNode).MassFlowRateMinAvail;
1002 :
1003 : // Do the following initializations (every time step): This should be the info from
1004 : // the previous components outlets or the node data in this section.
1005 : // Load the node data in this section for the component simulation
1006 67628 : this->dd_airterminalOAInlet.AirMassFlowRate = state.dataLoopNodes->Node(OAInNode).MassFlowRate;
1007 67628 : this->dd_airterminalOAInlet.AirTemp = state.dataLoopNodes->Node(OAInNode).Temp;
1008 67628 : this->dd_airterminalOAInlet.AirHumRat = state.dataLoopNodes->Node(OAInNode).HumRat;
1009 67628 : this->dd_airterminalOAInlet.AirEnthalpy = state.dataLoopNodes->Node(OAInNode).Enthalpy;
1010 67628 : if (this->RecircIsUsed) {
1011 36888 : this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail = state.dataLoopNodes->Node(RAInNode).MassFlowRateMaxAvail;
1012 36888 : this->dd_airterminalRecircAirInlet.AirMassFlowRateMinAvail = state.dataLoopNodes->Node(RAInNode).MassFlowRateMinAvail;
1013 36888 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(RAInNode).MassFlowRate;
1014 36888 : this->dd_airterminalRecircAirInlet.AirTemp = state.dataLoopNodes->Node(RAInNode).Temp;
1015 36888 : this->dd_airterminalRecircAirInlet.AirHumRat = state.dataLoopNodes->Node(RAInNode).HumRat;
1016 36888 : this->dd_airterminalRecircAirInlet.AirEnthalpy = state.dataLoopNodes->Node(RAInNode).Enthalpy;
1017 : }
1018 : }
1019 206846 : }
1020 :
1021 34 : void DualDuctAirTerminal::SizeDualDuct(EnergyPlusData &state)
1022 : {
1023 :
1024 : // SUBROUTINE INFORMATION:
1025 : // AUTHOR Fred Buhl
1026 : // DATE WRITTEN January 2002
1027 : // MODIFIED na
1028 : // RE-ENGINEERED na
1029 :
1030 : // PURPOSE OF THIS SUBROUTINE:
1031 : // This subroutine is for sizing Dual Duct air terminal units for which flow rates have not been
1032 : // specified in the input.
1033 :
1034 : // METHODOLOGY EMPLOYED:
1035 : // Obtains flow rates from the zone or system sizing arrays.
1036 :
1037 34 : if (this->MaxAirVolFlowRate == DataSizing::AutoSize) {
1038 :
1039 19 : if ((state.dataSize->CurZoneEqNum > 0) && (state.dataSize->CurTermUnitSizingNum > 0)) {
1040 19 : std::string_view damperType = cmoNameArray[static_cast<int>(this->DamperType)];
1041 : // ideally we'd just use a string_view, but there are multiple calls that are not yet set up for string_view, and they pass a
1042 : // reference, so we just create a string version for now. When we do more string_view cleanup, we'll end up searching on
1043 : // std::string() to find usages of it, so this should show up and get cleaned up then. Regardless, this is only called at
1044 : // program initialization, so it is not a runtime issue.
1045 38 : std::string damperTypeAsString = std::string(damperType);
1046 19 : CheckZoneSizing(state, damperTypeAsString, this->Name);
1047 19 : this->MaxAirVolFlowRate = max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
1048 19 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
1049 19 : if (this->DamperType == DualDuctDamper::OutdoorAir) {
1050 11 : if (this->RecircIsUsed) {
1051 6 : this->DesignRecircFlowRate =
1052 6 : max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
1053 6 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
1054 6 : this->MaxAirVolFlowRate = this->DesignRecircFlowRate + this->DesignOAFlowRate;
1055 : } else {
1056 5 : this->MaxAirVolFlowRate = this->DesignOAFlowRate;
1057 5 : this->DesignRecircFlowRate = 0.0;
1058 : }
1059 11 : this->MaxAirMassFlowRate = this->MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
1060 : }
1061 :
1062 19 : if (this->MaxAirVolFlowRate < DataHVACGlobals::SmallAirVolFlow) {
1063 0 : this->MaxAirVolFlowRate = 0.0;
1064 0 : this->MaxAirMassFlowRate = 0.0;
1065 0 : this->DesignOAFlowRate = 0.0;
1066 0 : this->DesignRecircFlowRate = 0.0;
1067 : }
1068 19 : BaseSizer::reportSizerOutput(state, damperTypeAsString, this->Name, "Maximum Air Flow Rate [m3/s]", this->MaxAirVolFlowRate);
1069 19 : if (this->DamperType == DualDuctDamper::OutdoorAir) {
1070 22 : BaseSizer::reportSizerOutput(
1071 11 : state, damperTypeAsString, this->Name, "Maximum Outdoor Air Flow Rate [m3/s]", this->DesignOAFlowRate);
1072 11 : if (this->RecircIsUsed) {
1073 12 : BaseSizer::reportSizerOutput(
1074 6 : state, damperTypeAsString, this->Name, "Maximum Recirculated Air Flow Rate [m3/s]", this->DesignRecircFlowRate);
1075 : }
1076 : }
1077 : }
1078 : }
1079 34 : }
1080 :
1081 122805 : void DualDuctAirTerminal::SimDualDuctConstVol(EnergyPlusData &state, int const ZoneNum, int const ZoneNodeNum)
1082 : {
1083 :
1084 : // SUBROUTINE INFORMATION:
1085 : // AUTHOR Richard J. Liesen
1086 : // DATE WRITTEN Jan 2000
1087 : // MODIFIED na
1088 : // RE-ENGINEERED na
1089 :
1090 : // PURPOSE OF THIS SUBROUTINE:
1091 : // This subroutine simulates the simple mixing damper.
1092 :
1093 : // METHODOLOGY EMPLOYED:
1094 : // There is method to this madness.
1095 :
1096 : // Using/Aliasing
1097 : using namespace DataZoneEnergyDemands;
1098 : using DataHVACGlobals::SmallTempDiff;
1099 : using Psychrometrics::PsyCpAirFnW;
1100 : using Psychrometrics::PsyTdbFnHW;
1101 :
1102 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1103 : Real64 MassFlow; // [kg/sec] Total Mass Flow Rate from Hot & Cold Inlets
1104 : Real64 HumRat; // [Kg Moisture / Kg dry air]
1105 : Real64 Enthalpy; // [Watts]
1106 : Real64 Temperature; // [C]
1107 : Real64 QTotLoad; // [W]
1108 : Real64 QZnReq; // [W]
1109 : Real64 CpAirZn;
1110 : Real64 CpAirSysHot;
1111 : Real64 CpAirSysCold;
1112 :
1113 : // Get the calculated load from the Heat Balance from ZoneSysEnergyDemand
1114 122805 : QTotLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
1115 : // Need the design MassFlowRate for calculations
1116 122805 : if (ScheduleManager::GetCurrentScheduleValue(state, this->SchedPtr) > 0.0) {
1117 98484 : MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail / 2.0 + this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail / 2.0;
1118 : } else {
1119 24321 : MassFlow = 0.0;
1120 : }
1121 : // If there is massflow then need to provide the correct amount of total
1122 : // required zone energy
1123 122805 : if (MassFlow > DataHVACGlobals::SmallMassFlow) {
1124 98337 : CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
1125 98337 : QZnReq = QTotLoad + MassFlow * CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp;
1126 : // If the enthalpy is the same for the hot and cold duct then there would be a
1127 : // divide by zero so for heating or cooling set the damper to one max flow
1128 : // or the other.
1129 98337 : if (std::abs(this->dd_airterminalColdAirInlet.AirTemp - this->dd_airterminalHotAirInlet.AirTemp) > SmallTempDiff) {
1130 : // CpAirSysHot = PsyCpAirFnWTdb(dd_airterminalHotAirInlet(DDNum)%AirHumRat,dd_airterminalHotAirInlet(DDNum)%AirTemp)
1131 : // CpAirSysCold= PsyCpAirFnWTdb(dd_airterminalColdAirInlet(DDNum)%AirHumRat,dd_airterminalColdAirInlet(DDNum)%AirTemp)
1132 97523 : CpAirSysHot = CpAirZn;
1133 97523 : CpAirSysCold = CpAirZn;
1134 : // Determine the Cold Air Mass Flow Rate
1135 97523 : this->dd_airterminalColdAirInlet.AirMassFlowRate =
1136 195046 : (QZnReq - MassFlow * CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) /
1137 97523 : (CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp - CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp);
1138 814 : } else if ((QTotLoad > 0.0) && (this->dd_airterminalHotAirInlet.AirMassFlowRate > 0.0)) {
1139 15 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1140 : } else {
1141 799 : this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
1142 : }
1143 : // Check to make sure that the calculated flow is not greater than the available flows
1144 98337 : if (this->dd_airterminalColdAirInlet.AirMassFlowRate > this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
1145 3938 : this->dd_airterminalColdAirInlet.AirMassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
1146 94399 : } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail) {
1147 9074 : this->dd_airterminalColdAirInlet.AirMassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail;
1148 : }
1149 : // Using Mass Continuity to determine the other duct flow quantity
1150 98337 : this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow - this->dd_airterminalColdAirInlet.AirMassFlowRate;
1151 98337 : if (this->dd_airterminalHotAirInlet.AirMassFlowRate > this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail) {
1152 0 : this->dd_airterminalHotAirInlet.AirMassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail;
1153 98337 : } else if (this->dd_airterminalHotAirInlet.AirMassFlowRate < this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail) {
1154 10 : this->dd_airterminalHotAirInlet.AirMassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail;
1155 : }
1156 98337 : MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRate + this->dd_airterminalHotAirInlet.AirMassFlowRate;
1157 : } else {
1158 : // System is Off set massflow to 0.0
1159 24468 : MassFlow = 0.0;
1160 : }
1161 122805 : if (MassFlow > DataHVACGlobals::SmallMassFlow) {
1162 : // After flows are calculated then calculate the mixed air flow properties.
1163 196674 : HumRat = (this->dd_airterminalHotAirInlet.AirHumRat * this->dd_airterminalHotAirInlet.AirMassFlowRate +
1164 98337 : this->dd_airterminalColdAirInlet.AirHumRat * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
1165 : MassFlow;
1166 196674 : Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy * this->dd_airterminalHotAirInlet.AirMassFlowRate +
1167 98337 : this->dd_airterminalColdAirInlet.AirEnthalpy * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
1168 : MassFlow;
1169 :
1170 : // If there is no air flow than calculate the No Flow conditions
1171 : } else {
1172 24468 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1173 24468 : this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
1174 24468 : HumRat = (this->dd_airterminalHotAirInlet.AirHumRat + this->dd_airterminalColdAirInlet.AirHumRat) / 2.0;
1175 24468 : Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy + this->dd_airterminalColdAirInlet.AirEnthalpy) / 2.0;
1176 : }
1177 122805 : Temperature = PsyTdbFnHW(Enthalpy, HumRat);
1178 :
1179 : // Load all properties in the damper outlet
1180 122805 : this->dd_airterminalOutlet.AirTemp = Temperature;
1181 122805 : this->dd_airterminalOutlet.AirHumRat = HumRat;
1182 122805 : this->dd_airterminalOutlet.AirMassFlowRate = MassFlow;
1183 122805 : this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlow;
1184 122805 : this->dd_airterminalOutlet.AirMassFlowRateMinAvail =
1185 122805 : min(this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail, this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail);
1186 122805 : this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
1187 :
1188 : // Calculate the hot and cold damper position in %
1189 122805 : if ((this->dd_airterminalHotAirInlet.AirMassFlowRateMax == 0.0) || (this->dd_airterminalColdAirInlet.AirMassFlowRateMax == 0.0)) {
1190 8 : this->ColdAirDamperPosition = 0.0;
1191 8 : this->HotAirDamperPosition = 0.0;
1192 : } else {
1193 122797 : this->ColdAirDamperPosition = this->dd_airterminalColdAirInlet.AirMassFlowRate / this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
1194 122797 : this->HotAirDamperPosition = this->dd_airterminalHotAirInlet.AirMassFlowRate / this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
1195 : }
1196 122805 : }
1197 :
1198 16413 : void DualDuctAirTerminal::SimDualDuctVarVol(EnergyPlusData &state, int const ZoneNum, int const ZoneNodeNum)
1199 : {
1200 :
1201 : // SUBROUTINE INFORMATION:
1202 : // AUTHOR Richard J. Liesen
1203 : // DATE WRITTEN Jan 2000
1204 : // MODIFIED na
1205 : // TH 3/2012: added supply air flow adjustment based on zone maximum outdoor
1206 : // air fraction - a TRACE feature
1207 : // RE-ENGINEERED na
1208 :
1209 : // PURPOSE OF THIS SUBROUTINE:
1210 : // This subroutine simulates the simple mixing damper.
1211 :
1212 : // METHODOLOGY EMPLOYED:
1213 : // There is method to this madness.
1214 :
1215 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1216 : Real64 MassFlow; // [kg/sec] Total Mass Flow Rate from Hot & Cold Inlets
1217 : Real64 HumRat; // [Kg Moisture / Kg dry air]
1218 : Real64 Enthalpy; // [Watts]
1219 : Real64 Temperature; // [C]
1220 : Real64 QTotLoad; // [W]
1221 : Real64 QZnReq; // [W]
1222 : Real64 CpAirZn; // specific heat of zone air
1223 : Real64 CpAirSysHot;
1224 : Real64 CpAirSysCold;
1225 : Real64 MassFlowBasedOnOA; // Supply air flow rate based on minimum OA requirement
1226 : Real64 AirLoopOAFrac; // fraction of outdoor air entering air loop outside air system
1227 :
1228 : // The calculated load from the Heat Balance
1229 16413 : QTotLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
1230 : // Calculate all of the required Cp's
1231 16413 : CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
1232 : // CpAirSysHot = PsyCpAirFnW(DamperHotAirInlet(DDNum)%AirHumRat,DamperHotAirInlet(DDNum)%AirTemp)
1233 : // CpAirSysCold= PsyCpAirFnW(DamperColdAirInlet(DDNum)%AirHumRat,DamperColdAirInlet(DDNum)%AirTemp)
1234 16413 : CpAirSysHot = CpAirZn;
1235 16413 : CpAirSysCold = CpAirZn;
1236 :
1237 : // calculate supply air flow rate based on user specified OA requirement
1238 16413 : this->CalcOAMassFlow(state, MassFlowBasedOnOA, AirLoopOAFrac);
1239 :
1240 : // Then depending on if the Load is for heating or cooling it is handled differently. First
1241 : // the massflow rate of either heating or cooling is determined to meet the entire load. Then
1242 : // if the massflow is below the minimum or greater than the Max it is set to either the Min
1243 : // or the Max as specified for the VAV model.
1244 16413 : if (ScheduleManager::GetCurrentScheduleValue(state, this->SchedPtr) == 0.0) {
1245 : // System is Off set massflow to 0.0
1246 6081 : MassFlow = 0.0;
1247 :
1248 10332 : } else if ((QTotLoad > 0.0) && (this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail > 0.0)) {
1249 : // Then heating is needed
1250 : // Next check for the denominator equal to zero
1251 13976 : if (std::abs((CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
1252 6988 : CpAirZn >
1253 : DataHVACGlobals::SmallTempDiff) {
1254 6988 : MassFlow = QTotLoad / (CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp - CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp);
1255 : } else {
1256 : // If denominator tends to zero then mass flow would go to infinity thus set to the max for this iteration
1257 0 : MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail;
1258 : }
1259 : // Check to see if the flow is < the Min or > the Max air Fraction to the zone; then set to min or max
1260 6988 : if (MassFlow <= (this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac)) {
1261 5089 : MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
1262 5089 : MassFlow = max(MassFlow, this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail);
1263 1899 : } else if (MassFlow >= this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail) {
1264 46 : MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail;
1265 : }
1266 :
1267 : // Apply the zone maximum outdoor air fraction for VAV boxes - a TRACE feature
1268 6988 : if (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor > 1.0) {
1269 0 : MassFlow *= state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor;
1270 : }
1271 :
1272 6988 : MassFlow = max(MassFlow, MassFlowBasedOnOA);
1273 6988 : MassFlow = min(MassFlow, this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail);
1274 :
1275 3344 : } else if ((QTotLoad < 0.0) && (this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail > 0.0)) {
1276 : // Then cooling is required
1277 : // Next check for the denominator equal to zero
1278 6670 : if (std::abs((CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
1279 3335 : CpAirZn >
1280 : DataHVACGlobals::SmallTempDiff) {
1281 3335 : MassFlow =
1282 3335 : QTotLoad / (CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp - CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp);
1283 : } else {
1284 : // If denominator tends to zero then mass flow would go to infinity thus set to the max for this iteration
1285 0 : MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
1286 : }
1287 :
1288 : // Check to see if the flow is < the Min or > the Max air Fraction to the zone; then set to min or max
1289 3335 : if ((MassFlow <= (this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac)) && (MassFlow >= 0.0)) {
1290 234 : MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
1291 234 : MassFlow = max(MassFlow, this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail);
1292 3101 : } else if (MassFlow < 0.0) {
1293 347 : MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
1294 2754 : } else if (MassFlow >= this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
1295 462 : MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
1296 : }
1297 :
1298 : // Apply the zone maximum outdoor air fraction for VAV boxes - a TRACE feature
1299 3335 : if (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor > 1.0) {
1300 0 : MassFlow *= state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor;
1301 : }
1302 :
1303 3335 : MassFlow = max(MassFlow, MassFlowBasedOnOA);
1304 3335 : MassFlow = min(MassFlow, this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail);
1305 :
1306 18 : } else if ((this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail > 0.0) ||
1307 9 : (this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail > 0.0)) {
1308 : // No Load on Zone set to mixed condition
1309 0 : MassFlow = (this->dd_airterminalHotAirInlet.AirMassFlowRateMax / 2.0) * this->ZoneMinAirFrac +
1310 0 : this->dd_airterminalColdAirInlet.AirMassFlowRateMax / 2.0 * this->ZoneMinAirFrac;
1311 :
1312 : // Apply the zone maximum outdoor air fraction for VAV boxes - a TRACE feature
1313 0 : if (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor > 1.0) {
1314 0 : MassFlow *= state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor;
1315 : }
1316 :
1317 0 : MassFlow = max(MassFlow, MassFlowBasedOnOA);
1318 0 : MassFlow =
1319 0 : min(MassFlow, (this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail + this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail));
1320 :
1321 : } else {
1322 : // System is Off set massflow to 0.0
1323 9 : MassFlow = 0.0;
1324 : }
1325 :
1326 : // Now the massflow for heating or cooling has been determined and if the massflow was reset to the
1327 : // Min or Max we will need to mix the hot and cold deck to meet the zone load. Knowing the enthalpy
1328 : // of the zone and the hot and cold air flows we can determine exactly by using the Energy and Continuity
1329 : // Eqns. Of course we have to make sure that we are within the Min and Max flow conditions.
1330 16413 : if (MassFlow > DataHVACGlobals::SmallMassFlow) {
1331 : // Determine the enthalpy required from Zone enthalpy and the zone load.
1332 10323 : QZnReq = QTotLoad + MassFlow * CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp;
1333 : // Using the known enthalpies the cold air inlet mass flow is determined. If the enthalpy of the hot and cold
1334 : // air streams are equal the IF-Then block handles that condition.
1335 10323 : if (std::abs(this->dd_airterminalColdAirInlet.AirTemp - this->dd_airterminalHotAirInlet.AirTemp) > DataHVACGlobals::SmallTempDiff) {
1336 : // Calculate the Cold air mass flow rate
1337 10152 : this->dd_airterminalColdAirInlet.AirMassFlowRate =
1338 20304 : (QZnReq - MassFlow * CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) /
1339 10152 : (CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp - CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp);
1340 171 : } else if ((QTotLoad > 0.0) && (this->dd_airterminalHotAirInlet.AirMassFlowRate > 0.0)) {
1341 0 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1342 : } else {
1343 171 : this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
1344 : }
1345 :
1346 : // Need to make sure that the flows are within limits
1347 10323 : if (this->dd_airterminalColdAirInlet.AirMassFlowRate > this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
1348 639 : this->dd_airterminalColdAirInlet.AirMassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
1349 :
1350 : // These are shutoff boxes for either the hot or the cold, therfore one side or other can = 0.0
1351 9684 : } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < 0.0) {
1352 368 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1353 9316 : } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate > MassFlow) {
1354 688 : this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
1355 : }
1356 : // Using Mass Continuity to determine the other duct flow quantity
1357 10323 : this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow - this->dd_airterminalColdAirInlet.AirMassFlowRate;
1358 :
1359 10323 : if (this->dd_airterminalHotAirInlet.AirMassFlowRate < DualDuctMassFlowSetToler) {
1360 3109 : this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
1361 3109 : this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
1362 7214 : } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < DualDuctMassFlowSetToler) {
1363 1899 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1364 1899 : this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow;
1365 : }
1366 :
1367 : // After the flow rates are determined the properties are calculated.
1368 20646 : HumRat = (this->dd_airterminalHotAirInlet.AirHumRat * this->dd_airterminalHotAirInlet.AirMassFlowRate +
1369 10323 : this->dd_airterminalColdAirInlet.AirHumRat * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
1370 : MassFlow;
1371 20646 : Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy * this->dd_airterminalHotAirInlet.AirMassFlowRate +
1372 10323 : this->dd_airterminalColdAirInlet.AirEnthalpy * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
1373 : MassFlow;
1374 :
1375 : // IF the system is OFF the properties are calculated for this special case.
1376 : } else {
1377 6090 : this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
1378 6090 : this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
1379 6090 : HumRat = (this->dd_airterminalHotAirInlet.AirHumRat + this->dd_airterminalColdAirInlet.AirHumRat) / 2.0;
1380 6090 : Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy + this->dd_airterminalColdAirInlet.AirEnthalpy) / 2.0;
1381 : }
1382 16413 : Temperature = Psychrometrics::PsyTdbFnHW(Enthalpy, HumRat);
1383 :
1384 16413 : this->dd_airterminalOutlet.AirTemp = Temperature;
1385 16413 : this->dd_airterminalOutlet.AirHumRat = HumRat;
1386 16413 : this->dd_airterminalOutlet.AirMassFlowRate = MassFlow;
1387 16413 : this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlow;
1388 16413 : this->dd_airterminalOutlet.AirMassFlowRateMinAvail = this->ZoneMinAirFrac * this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
1389 16413 : this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
1390 :
1391 : // Calculate the hot and cold damper position in %
1392 16413 : if ((this->dd_airterminalHotAirInlet.AirMassFlowRateMax == 0.0) || (this->dd_airterminalColdAirInlet.AirMassFlowRateMax == 0.0)) {
1393 0 : this->ColdAirDamperPosition = 0.0;
1394 0 : this->HotAirDamperPosition = 0.0;
1395 : } else {
1396 16413 : this->ColdAirDamperPosition = this->dd_airterminalColdAirInlet.AirMassFlowRate / this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
1397 16413 : this->HotAirDamperPosition = this->dd_airterminalHotAirInlet.AirMassFlowRate / this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
1398 : }
1399 16413 : }
1400 :
1401 67628 : void DualDuctAirTerminal::SimDualDuctVAVOutdoorAir(EnergyPlusData &state, int const ZoneNum, int const ZoneNodeNum)
1402 : {
1403 :
1404 : // SUBROUTINE INFORMATION:
1405 : // AUTHOR Clayton Miller
1406 : // DATE WRITTEN Aug 2010
1407 : // MODIFIED B. Griffith, Dec 2010, major rework
1408 : // RE-ENGINEERED na
1409 :
1410 : // PURPOSE OF THIS SUBROUTINE:
1411 : // Designed to accommodate for systems with outdoor air (OA) and recirculated air (RA)
1412 : // as two separate air streams to controlled at the zone level in a dual duct system.
1413 :
1414 : // METHODOLOGY EMPLOYED:
1415 : // The terminal unit is be designed to set the airflow of the of the OA stream at the zone
1416 : // level based on the zonal ventilation requirements and the RA stream flowrate of recirculated
1417 : // cooling air stream in order to meet the remaining thermal load.
1418 : // If the zone calls for cooling but the inlet air temperature is too warm, recirc side set to zero
1419 : // if the zone calls for heating and the inlet air is warm enough, modulate damper to meet load
1420 : // if the zone calls for heating and the inlet air is too cold, zero flow (will not control sans reheat)
1421 :
1422 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1423 : Real64 MassFlowMax; // [kg/sec] Maximum Mass Flow Rate from OA and Recirc Inlets
1424 : Real64 HumRat; // [Kg Moisture / Kg dry air]
1425 : Real64 Enthalpy; // [Watts]
1426 : Real64 Temperature; // [C]
1427 : Real64 QTotLoadRemain; // [W]
1428 : Real64 QtoHeatSPRemain; // [W]
1429 : Real64 QtoCoolSPRemain; // [W]
1430 : // REAL(r64) :: QTotRemainAdjust ! [W]
1431 : Real64 QtoHeatSPRemainAdjust; // [W]
1432 : Real64 QtoCoolSPRemainAdjust; // [W]
1433 : Real64 QOALoadToHeatSP; // [W]
1434 : Real64 QOALoadToCoolSP; // [W]
1435 : Real64 QOALoad; // Amount of cooling load accounted for by OA Stream [W]
1436 : Real64 QRALoad; // Amount of cooling load accounted for by Recirc Stream [W]
1437 : Real64 CpAirZn; // specific heat of zone air
1438 : Real64 CpAirSysOA; // specific heat of outdoor air
1439 : Real64 CpAirSysRA; // specific heat of recirculated air
1440 : Real64 OAMassFlow; // Supply air flow rate based on minimum OA requirement - for printing
1441 : Real64 TotMassFlow; // [kg/sec] Total Mass Flow Rate from OA and Recirc Inlets
1442 : int OAInletNodeNum;
1443 : int RecircInletNodeNum;
1444 :
1445 67628 : OAInletNodeNum = this->OAInletNodeNum;
1446 67628 : if (this->RecircIsUsed) {
1447 36888 : RecircInletNodeNum = this->RecircAirInletNodeNum;
1448 : }
1449 : // Calculate required ventilation air flow rate based on user specified OA requirement
1450 67628 : this->CalcOAOnlyMassFlow(state, OAMassFlow);
1451 :
1452 : // The calculated load from the Heat Balance, adjusted for any equipment sequenced before terminal
1453 67628 : QTotLoadRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
1454 67628 : QtoHeatSPRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP;
1455 67628 : QtoCoolSPRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToCoolSP;
1456 :
1457 : // Calculate all of the required Cp's
1458 67628 : CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
1459 67628 : CpAirSysOA = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(OAInletNodeNum).HumRat);
1460 67628 : if (this->RecircIsUsed) CpAirSysRA = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(RecircInletNodeNum).HumRat);
1461 :
1462 : // Set the OA Damper to the calculated ventilation flow rate
1463 67628 : this->dd_airterminalOAInlet.AirMassFlowRate = OAMassFlow;
1464 : // Need to make sure that the OA flows are within limits
1465 67628 : if (this->dd_airterminalOAInlet.AirMassFlowRate > this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail) {
1466 66 : this->dd_airterminalOAInlet.AirMassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail;
1467 67562 : } else if (this->dd_airterminalOAInlet.AirMassFlowRate < 0.0) {
1468 0 : this->dd_airterminalOAInlet.AirMassFlowRate = 0.0;
1469 : }
1470 :
1471 : //..Find the amount of load that the OAMassFlow accounted for
1472 67628 : if (std::abs((CpAirSysOA * this->dd_airterminalOAInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) / CpAirZn >
1473 : DataHVACGlobals::SmallTempDiff) {
1474 135086 : QOALoad = this->dd_airterminalOAInlet.AirMassFlowRate *
1475 67543 : (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp - CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp);
1476 :
1477 135086 : QOALoadToHeatSP = this->dd_airterminalOAInlet.AirMassFlowRate * (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp -
1478 67543 : CpAirZn * state.dataHeatBalFanSys->ZoneThermostatSetPointLo(ZoneNum));
1479 135086 : QOALoadToCoolSP = this->dd_airterminalOAInlet.AirMassFlowRate * (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp -
1480 67543 : CpAirZn * state.dataHeatBalFanSys->ZoneThermostatSetPointHi(ZoneNum));
1481 :
1482 : } else {
1483 85 : QOALoad = 0.0;
1484 85 : QOALoadToHeatSP = 0.0;
1485 85 : QOALoadToCoolSP = 0.0;
1486 : }
1487 :
1488 67628 : if (this->RecircIsUsed) {
1489 :
1490 : // correct load for recirc side to account for impact of OA side
1491 : // QTotRemainAdjust = QTotLoadRemain - QOALoad
1492 36888 : QtoHeatSPRemainAdjust = QtoHeatSPRemain - QOALoadToHeatSP;
1493 36888 : QtoCoolSPRemainAdjust = QtoCoolSPRemain - QOALoadToCoolSP;
1494 :
1495 36888 : if (QtoCoolSPRemainAdjust < 0.0) {
1496 23594 : QRALoad = QtoCoolSPRemainAdjust;
1497 13294 : } else if (QtoHeatSPRemainAdjust > 0.0) {
1498 12180 : QRALoad = QtoHeatSPRemainAdjust;
1499 : } else {
1500 1114 : QRALoad = 0.0;
1501 : }
1502 :
1503 36888 : if (QRALoad < 0.0) { // cooling
1504 23594 : if ((this->dd_airterminalRecircAirInlet.AirTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp) < -0.5) { // can cool
1505 : // Find the Mass Flow Rate of the RA Stream needed to meet the zone cooling load
1506 70782 : if (std::abs((CpAirSysRA * this->dd_airterminalRecircAirInlet.AirTemp) -
1507 47188 : (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
1508 23594 : CpAirZn >
1509 : DataHVACGlobals::SmallTempDiff) {
1510 47188 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = QRALoad / (CpAirSysRA * this->dd_airterminalRecircAirInlet.AirTemp -
1511 23594 : CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp);
1512 : }
1513 : } else {
1514 0 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
1515 : }
1516 :
1517 : } else { // heating or none needed.
1518 13294 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
1519 : }
1520 :
1521 : // Need to make sure that the RA flows are within limits
1522 36888 : if (this->dd_airterminalRecircAirInlet.AirMassFlowRate > this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail) {
1523 25 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail;
1524 : // These are shutoff boxes for either the hot or the cold, therfore one side or other can = 0.0
1525 36863 : } else if (this->dd_airterminalRecircAirInlet.AirMassFlowRate < 0.0) {
1526 0 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
1527 : }
1528 :
1529 : } else {
1530 30740 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
1531 30740 : this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail = 0.0;
1532 : } // recirc used
1533 :
1534 : // look for bang-bang condition: flow rate oscillating between 2 values during the air loop / zone
1535 : // equipment iteration. If detected, set flow rate to previous value.
1536 135256 : if (((std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2) <
1537 121947 : this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag) ||
1538 54319 : (std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3) <
1539 135256 : this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag)) &&
1540 13309 : (std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1) >=
1541 13309 : this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag)) {
1542 16 : if (this->dd_airterminalRecircAirInlet.AirMassFlowRate > 0.0) {
1543 0 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1;
1544 : }
1545 : }
1546 :
1547 : // Find the Max Box Flow Rate.
1548 67628 : MassFlowMax = this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail + this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail;
1549 67628 : if (ScheduleManager::GetCurrentScheduleValue(state, this->SchedPtr) > 0.0) {
1550 67628 : TotMassFlow = this->dd_airterminalOAInlet.AirMassFlowRate + this->dd_airterminalRecircAirInlet.AirMassFlowRate;
1551 : } else {
1552 0 : TotMassFlow = 0.0;
1553 : }
1554 :
1555 67628 : if (TotMassFlow > DataHVACGlobals::SmallMassFlow) {
1556 :
1557 : // If the sum of the two air streams' flow is greater than the Max Box Flow Rate then reset the RA Stream
1558 67408 : if (TotMassFlow > MassFlowMax) {
1559 0 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = MassFlowMax - this->dd_airterminalOAInlet.AirMassFlowRate;
1560 : }
1561 : // After the flow rates are determined the properties are calculated.
1562 67408 : TotMassFlow = this->dd_airterminalOAInlet.AirMassFlowRate + this->dd_airterminalRecircAirInlet.AirMassFlowRate;
1563 67408 : if (TotMassFlow > DataHVACGlobals::SmallMassFlow) {
1564 134816 : HumRat = (this->dd_airterminalOAInlet.AirHumRat * this->dd_airterminalOAInlet.AirMassFlowRate +
1565 67408 : this->dd_airterminalRecircAirInlet.AirHumRat * this->dd_airterminalRecircAirInlet.AirMassFlowRate) /
1566 : TotMassFlow;
1567 134816 : Enthalpy = (this->dd_airterminalOAInlet.AirEnthalpy * this->dd_airterminalOAInlet.AirMassFlowRate +
1568 67408 : this->dd_airterminalRecircAirInlet.AirEnthalpy * this->dd_airterminalRecircAirInlet.AirMassFlowRate) /
1569 : TotMassFlow;
1570 : } else {
1571 0 : HumRat = (this->dd_airterminalRecircAirInlet.AirHumRat + this->dd_airterminalOAInlet.AirHumRat) / 2.0;
1572 0 : Enthalpy = (this->dd_airterminalRecircAirInlet.AirEnthalpy + this->dd_airterminalOAInlet.AirEnthalpy) / 2.0;
1573 : }
1574 : } else {
1575 :
1576 : // The Max Box Flow Rate is zero and the box is off.
1577 220 : this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
1578 220 : this->dd_airterminalOAInlet.AirMassFlowRate = 0.0;
1579 220 : HumRat = (this->dd_airterminalRecircAirInlet.AirHumRat + this->dd_airterminalOAInlet.AirHumRat) / 2.0;
1580 220 : Enthalpy = (this->dd_airterminalRecircAirInlet.AirEnthalpy + this->dd_airterminalOAInlet.AirEnthalpy) / 2.0;
1581 : }
1582 :
1583 67628 : Temperature = Psychrometrics::PsyTdbFnHW(Enthalpy, HumRat);
1584 :
1585 67628 : this->dd_airterminalOutlet.AirTemp = Temperature;
1586 67628 : this->dd_airterminalOutlet.AirHumRat = HumRat;
1587 67628 : this->dd_airterminalOutlet.AirMassFlowRate = TotMassFlow;
1588 67628 : this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlowMax;
1589 67628 : this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
1590 :
1591 : // Calculate the OA and RA damper position in %
1592 67628 : if (this->RecircIsUsed) {
1593 36888 : if (this->dd_airterminalRecircAirInlet.AirMassFlowRateMax == 0.0) { // protect div by zero
1594 6 : this->RecircAirDamperPosition = 0.0;
1595 : } else {
1596 36882 : this->RecircAirDamperPosition =
1597 36882 : this->dd_airterminalRecircAirInlet.AirMassFlowRate / this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
1598 : }
1599 : }
1600 :
1601 67628 : if (this->dd_airterminalOAInlet.AirMassFlowRateMax == 0.0) { // protect div by zero
1602 11 : this->OADamperPosition = 0.0;
1603 : } else {
1604 67617 : this->OADamperPosition = this->dd_airterminalOAInlet.AirMassFlowRate / this->dd_airterminalOAInlet.AirMassFlowRateMax;
1605 : }
1606 :
1607 : // Calculate OAFraction of mixed air after the box
1608 67628 : if (TotMassFlow > 0) {
1609 67408 : if (this->RecircIsUsed) {
1610 36768 : if (this->dd_airterminalOAInlet.AirMassFlowRate == 0.0) {
1611 0 : this->OAFraction = 0.0;
1612 36768 : } else if (this->dd_airterminalRecircAirInlet.AirMassFlowRate == 0.0) {
1613 13193 : this->OAFraction = 1.0;
1614 : } else {
1615 23575 : this->OAFraction = this->dd_airterminalOAInlet.AirMassFlowRate / TotMassFlow;
1616 : }
1617 : } else {
1618 30640 : this->OAFraction = 1.0;
1619 : }
1620 : } else {
1621 220 : this->OAFraction = 0.0;
1622 : }
1623 :
1624 67628 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3 = this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2;
1625 67628 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2 = this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1;
1626 67628 : this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1 = this->dd_airterminalRecircAirInlet.AirMassFlowRate;
1627 67628 : }
1628 :
1629 16413 : void DualDuctAirTerminal::CalcOAMassFlow(EnergyPlusData &state, // NOLINT(readability-make-member-function-const)
1630 : Real64 &SAMassFlow, // outside air based on optional user input
1631 : Real64 &AirLoopOAFrac // outside air based on optional user input
1632 : )
1633 : {
1634 :
1635 : // FUNCTION INFORMATION:
1636 : // AUTHOR R. Raustad (FSEC)
1637 : // DATE WRITTEN Mar 2010
1638 : // MODIFIED Mangesh Basarkar, 06/2011: Modifying outside air based on airloop DCV flag
1639 : // RE-ENGINEERED na
1640 :
1641 : // PURPOSE OF THIS FUNCTION:
1642 : // Calculates the amount of outside air required based on optional user input.
1643 : // Zone multipliers are included and are applied in GetInput.
1644 :
1645 : // METHODOLOGY EMPLOYED:
1646 : // User input defines method used to calculate OA.
1647 :
1648 : // initialize OA flow rate and OA report variable
1649 16413 : SAMassFlow = 0.0;
1650 16413 : AirLoopOAFrac = 0.0;
1651 :
1652 : // Calculate the amount of OA based on optional user inputs
1653 16413 : if (AirLoopNum > 0) {
1654 16410 : AirLoopOAFrac = state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFrac;
1655 : // If no additional input from user, RETURN from subroutine
1656 16410 : if (this->NoOAFlowInputFromUser) return;
1657 : // Calculate outdoor air flow rate, zone multipliers are applied in GetInput
1658 0 : if (AirLoopOAFrac > 0.0) {
1659 0 : bool constexpr UseMinOASchFlag(true); // Always use min OA schedule in calculations.
1660 : Real64 const OAVolumeFlowRate =
1661 0 : DataSizing::calcDesignSpecificationOutdoorAir(state,
1662 : this->OARequirementsPtr,
1663 : this->CtrlZoneNum,
1664 0 : state.dataAirLoop->AirLoopControlInfo(AirLoopNum).AirLoopDCVFlag,
1665 0 : UseMinOASchFlag);
1666 0 : Real64 const OAMassFlow = OAVolumeFlowRate * state.dataEnvrn->StdRhoAir;
1667 :
1668 : // convert OA mass flow rate to supply air flow rate based on air loop OA fraction
1669 0 : SAMassFlow = OAMassFlow / AirLoopOAFrac;
1670 : }
1671 : }
1672 : }
1673 :
1674 67639 : void DualDuctAirTerminal::CalcOAOnlyMassFlow(EnergyPlusData &state, // NOLINT(readability-make-member-function-const)
1675 : Real64 &OAMassFlow, // outside air flow from user input kg/s
1676 : Optional<Real64> MaxOAVolFlow // design level for outside air m3/s
1677 : )
1678 : {
1679 :
1680 : // FUNCTION INFORMATION:
1681 : // AUTHOR C. Miller (Mod of CaclOAMassFlow by R. Raustad (FSEC))
1682 : // DATE WRITTEN Aug 2010
1683 : // MODIFIED B. Griffith, Dec 2010 clean up, sizing optional, scheduled OA
1684 : // RE-ENGINEERED na
1685 :
1686 : // PURPOSE OF THIS FUNCTION:
1687 : // Calculates the amount of outside air required based on optional user input. Returns
1688 : // ONLY calculated OAMassFlow without consideration of AirLoopOAFrac. Used for
1689 : // the DualDuct:VAV:OutdoorAir object which does not mix OA with RA
1690 :
1691 : // METHODOLOGY EMPLOYED:
1692 : // User input defines method used to calculate OA.
1693 :
1694 : // Calculate the amount of OA based on optional user inputs
1695 67639 : OAMassFlow = 0.0;
1696 :
1697 : // If no additional input from user, RETURN from subroutine
1698 67639 : if (this->NoOAFlowInputFromUser) {
1699 0 : ShowSevereError(
1700 0 : state, "CalcOAOnlyMassFlow: Problem in AirTerminal:DualDuct:VAV:OutdoorAir = " + this->Name + ", check outdoor air specification");
1701 0 : if (present(MaxOAVolFlow)) MaxOAVolFlow = 0.0;
1702 0 : return;
1703 : }
1704 :
1705 67639 : bool UseOccSchFlag = this->OAPerPersonMode == PerPersonMode::DCVByCurrentLevel; // TRUE = use actual occupancy, FALSE = use total zone people
1706 67639 : bool PerPersonNotSet = this->OAPerPersonMode != PerPersonMode::DCVByCurrentLevel && this->OAPerPersonMode != PerPersonMode::ByDesignLevel;
1707 :
1708 67639 : bool constexpr UseMinOASchFlag(true); // Always use min OA schedule in calculations.
1709 67639 : Real64 OAVolumeFlowRate = DataSizing::calcDesignSpecificationOutdoorAir(state,
1710 : this->OARequirementsPtr,
1711 : this->CtrlZoneNum,
1712 : UseOccSchFlag,
1713 : UseMinOASchFlag,
1714 67639 : PerPersonNotSet); // outside air volume flow rate (m3/s)
1715 :
1716 67639 : OAMassFlow = OAVolumeFlowRate * state.dataEnvrn->StdRhoAir;
1717 :
1718 67639 : if (present(MaxOAVolFlow)) {
1719 11 : OAVolumeFlowRate = DataSizing::calcDesignSpecificationOutdoorAir(
1720 : state, this->OARequirementsPtr, this->CtrlZoneNum, UseOccSchFlag, UseMinOASchFlag, false, true);
1721 11 : MaxOAVolFlow = OAVolumeFlowRate;
1722 : }
1723 : }
1724 :
1725 206846 : void DualDuctAirTerminal::UpdateDualDuct(EnergyPlusData &state)
1726 : {
1727 :
1728 : // SUBROUTINE INFORMATION:
1729 : // AUTHOR Richard J. Liesen
1730 : // DATE WRITTEN February 2000
1731 : // MODIFIED Aug 2010 Clayton Miller - Added DualDuctVAVOutdoorAir
1732 : // RE-ENGINEERED na
1733 :
1734 : // PURPOSE OF THIS SUBROUTINE:
1735 : // This subroutine updates the dampers.
1736 :
1737 206846 : if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
1738 :
1739 139218 : int OutletNode = this->OutletNodeNum;
1740 139218 : int HotInletNode = this->HotAirInletNodeNum;
1741 139218 : int ColdInletNode = this->ColdAirInletNodeNum;
1742 :
1743 : // Set the outlet air nodes of the Damper
1744 139218 : state.dataLoopNodes->Node(HotInletNode).MassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRate;
1745 139218 : state.dataLoopNodes->Node(ColdInletNode).MassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRate;
1746 139218 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = this->dd_airterminalOutlet.AirMassFlowRate;
1747 139218 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = this->dd_airterminalOutlet.AirMassFlowRate;
1748 139218 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = this->dd_airterminalOutlet.AirMassFlowRateMinAvail;
1749 139218 : state.dataLoopNodes->Node(OutletNode).Temp = this->dd_airterminalOutlet.AirTemp;
1750 139218 : state.dataLoopNodes->Node(OutletNode).HumRat = this->dd_airterminalOutlet.AirHumRat;
1751 139218 : state.dataLoopNodes->Node(OutletNode).Enthalpy = this->dd_airterminalOutlet.AirEnthalpy;
1752 : // Set the outlet nodes for properties that just pass through & not used
1753 : // FIX THIS LATER!!!!
1754 139218 : state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(HotInletNode).Quality;
1755 139218 : state.dataLoopNodes->Node(OutletNode).Press = state.dataLoopNodes->Node(HotInletNode).Press;
1756 :
1757 139218 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1758 0 : if (state.dataLoopNodes->Node(OutletNode).MassFlowRate > 0.0) {
1759 0 : state.dataLoopNodes->Node(OutletNode).CO2 =
1760 0 : (state.dataLoopNodes->Node(HotInletNode).CO2 * state.dataLoopNodes->Node(HotInletNode).MassFlowRate +
1761 0 : state.dataLoopNodes->Node(ColdInletNode).CO2 * state.dataLoopNodes->Node(ColdInletNode).MassFlowRate) /
1762 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1763 : } else {
1764 0 : state.dataLoopNodes->Node(OutletNode).CO2 =
1765 0 : max(state.dataLoopNodes->Node(HotInletNode).CO2, state.dataLoopNodes->Node(ColdInletNode).CO2);
1766 : }
1767 : }
1768 139218 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1769 0 : if (state.dataLoopNodes->Node(OutletNode).MassFlowRate > 0.0) {
1770 0 : state.dataLoopNodes->Node(OutletNode).GenContam =
1771 0 : (state.dataLoopNodes->Node(HotInletNode).GenContam * state.dataLoopNodes->Node(HotInletNode).MassFlowRate +
1772 0 : state.dataLoopNodes->Node(ColdInletNode).GenContam * state.dataLoopNodes->Node(ColdInletNode).MassFlowRate) /
1773 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1774 : } else {
1775 0 : state.dataLoopNodes->Node(OutletNode).GenContam =
1776 0 : max(state.dataLoopNodes->Node(HotInletNode).GenContam, state.dataLoopNodes->Node(ColdInletNode).GenContam);
1777 : }
1778 : }
1779 :
1780 139218 : this->CalcOutdoorAirVolumeFlowRate(state);
1781 :
1782 67628 : } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
1783 :
1784 67628 : int OutletNode = this->OutletNodeNum;
1785 67628 : int OAInletNode = this->OAInletNodeNum;
1786 : // Set the outlet air nodes of the Damper
1787 67628 : state.dataLoopNodes->Node(OAInletNode).MassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRate;
1788 67628 : state.dataLoopNodes->Node(OutletNode).MassFlowRate = this->dd_airterminalOutlet.AirMassFlowRate;
1789 67628 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = this->dd_airterminalOutlet.AirMassFlowRate;
1790 67628 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = this->dd_airterminalOutlet.AirMassFlowRateMinAvail;
1791 67628 : state.dataLoopNodes->Node(OutletNode).Temp = this->dd_airterminalOutlet.AirTemp;
1792 67628 : state.dataLoopNodes->Node(OutletNode).HumRat = this->dd_airterminalOutlet.AirHumRat;
1793 67628 : state.dataLoopNodes->Node(OutletNode).Enthalpy = this->dd_airterminalOutlet.AirEnthalpy;
1794 : // Set the outlet nodes for properties that just pass through & not used
1795 : // FIX THIS LATER!!!!
1796 67628 : state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(OAInletNode).Quality;
1797 67628 : state.dataLoopNodes->Node(OutletNode).Press = state.dataLoopNodes->Node(OAInletNode).Press;
1798 :
1799 67628 : if (this->RecircIsUsed) {
1800 36888 : int RAInletNode = this->RecircAirInletNodeNum;
1801 36888 : state.dataLoopNodes->Node(RAInletNode).MassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRate;
1802 36888 : if (state.dataLoopNodes->Node(OutletNode).MassFlowRate > 0.0) {
1803 36768 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1804 0 : state.dataLoopNodes->Node(OutletNode).CO2 =
1805 0 : (state.dataLoopNodes->Node(OAInletNode).CO2 * state.dataLoopNodes->Node(OAInletNode).MassFlowRate +
1806 0 : state.dataLoopNodes->Node(RAInletNode).CO2 * state.dataLoopNodes->Node(RAInletNode).MassFlowRate) /
1807 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1808 : }
1809 36768 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1810 0 : state.dataLoopNodes->Node(OutletNode).GenContam =
1811 0 : (state.dataLoopNodes->Node(OAInletNode).GenContam * state.dataLoopNodes->Node(OAInletNode).MassFlowRate +
1812 0 : state.dataLoopNodes->Node(RAInletNode).GenContam * state.dataLoopNodes->Node(RAInletNode).MassFlowRate) /
1813 0 : state.dataLoopNodes->Node(OutletNode).MassFlowRate;
1814 : }
1815 : } else {
1816 120 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1817 0 : state.dataLoopNodes->Node(OutletNode).CO2 =
1818 0 : max(state.dataLoopNodes->Node(OAInletNode).CO2, state.dataLoopNodes->Node(RAInletNode).CO2);
1819 : }
1820 120 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1821 0 : state.dataLoopNodes->Node(OutletNode).GenContam =
1822 0 : max(state.dataLoopNodes->Node(OAInletNode).GenContam, state.dataLoopNodes->Node(RAInletNode).GenContam);
1823 : }
1824 : }
1825 :
1826 : } else {
1827 30740 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1828 0 : state.dataLoopNodes->Node(OutletNode).CO2 = state.dataLoopNodes->Node(OAInletNode).CO2;
1829 : }
1830 30740 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1831 0 : state.dataLoopNodes->Node(OutletNode).GenContam = state.dataLoopNodes->Node(OAInletNode).GenContam;
1832 : }
1833 : }
1834 : }
1835 206846 : }
1836 :
1837 769 : void ReportDualDuctConnections(EnergyPlusData &state)
1838 : {
1839 :
1840 : // SUBROUTINE INFORMATION:
1841 : // AUTHOR Michael J. Witte
1842 : // DATE WRITTEN February 2004
1843 : // MODIFIED B. Griffith, DOAS VAV dual duct
1844 : // RE-ENGINEERED na
1845 :
1846 : // PURPOSE OF THIS SUBROUTINE:
1847 : // Report dual duct damper connections to the BND file.
1848 :
1849 : // Using/Aliasing
1850 769 : auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
1851 :
1852 : // Formats
1853 : static constexpr std::string_view Format_100("! <#Dual Duct Damper Connections>,<Number of Dual Duct Damper Connections>");
1854 : static constexpr std::string_view Format_102(
1855 : "! <Dual Duct Damper>,<Dual Duct Damper Count>,<Dual Duct Damper Name>,<Inlet Node>,<Outlet Node>,<Inlet "
1856 : "Node Type>,<AirLoopHVAC Name>");
1857 :
1858 769 : if (!allocated(state.dataDualDuct->dd_airterminal))
1859 761 : return; // Autodesk Bug: Can arrive here with Damper unallocated (SimulateDualDuct not yet called) with NumDDAirTerminal either set >0 or
1860 : // uninitialized
1861 :
1862 : // Report Dual Duct Dampers to BND File
1863 8 : print(state.files.bnd, "{}\n", "! ===============================================================");
1864 8 : print(state.files.bnd, "{}\n", Format_100);
1865 8 : print(state.files.bnd, " #Dual Duct Damper Connections,{}\n", state.dataDualDuct->NumDDAirTerminal * 2);
1866 8 : print(state.files.bnd, "{}\n", Format_102);
1867 :
1868 42 : for (int Count1 = 1; Count1 <= state.dataDualDuct->NumDDAirTerminal; ++Count1) {
1869 :
1870 : // Determine if this damper is connected to a supply air path
1871 34 : int Found = 0;
1872 34 : int SupplyAirPathNum = 0;
1873 40 : for (int Count2 = 1; Count2 <= state.dataZoneEquip->NumSupplyAirPaths; ++Count2) {
1874 40 : SupplyAirPathNum = Count2;
1875 40 : Found = 0;
1876 210 : for (int Count3 = 1; Count3 <= state.dataZoneEquip->SupplyAirPath(Count2).NumOutletNodes; ++Count3) {
1877 340 : if (state.dataDualDuct->dd_airterminal(Count1).HotAirInletNodeNum ==
1878 170 : state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3))
1879 23 : Found = Count3;
1880 340 : if (state.dataDualDuct->dd_airterminal(Count1).ColdAirInletNodeNum ==
1881 170 : state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3))
1882 0 : Found = Count3;
1883 170 : if (state.dataDualDuct->dd_airterminal(Count1).OAInletNodeNum == state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3))
1884 11 : Found = Count3;
1885 340 : if (state.dataDualDuct->dd_airterminal(Count1).RecircAirInletNodeNum ==
1886 170 : state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3))
1887 0 : Found = Count3;
1888 : }
1889 40 : if (Found != 0) break;
1890 : }
1891 34 : if (Found == 0) SupplyAirPathNum = 0;
1892 :
1893 : // Determine which air loop this dual duct damper is connected to
1894 34 : Found = 0;
1895 68 : std::string ChrName;
1896 40 : for (int Count2 = 1; Count2 <= NumPrimaryAirSys; ++Count2) {
1897 40 : ChrName = state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName;
1898 40 : Found = 0;
1899 109 : for (int Count3 = 1; Count3 <= state.dataAirLoop->AirToZoneNodeInfo(Count2).NumSupplyNodes; ++Count3) {
1900 69 : if (SupplyAirPathNum != 0) {
1901 138 : if (state.dataZoneEquip->SupplyAirPath(SupplyAirPathNum).InletNodeNum ==
1902 69 : state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3))
1903 34 : Found = Count3;
1904 : } else {
1905 0 : if (state.dataDualDuct->dd_airterminal(Count1).HotAirInletNodeNum ==
1906 0 : state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3))
1907 0 : Found = Count3;
1908 0 : if (state.dataDualDuct->dd_airterminal(Count1).ColdAirInletNodeNum ==
1909 0 : state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3))
1910 0 : Found = Count3;
1911 0 : if (state.dataDualDuct->dd_airterminal(Count1).OAInletNodeNum ==
1912 0 : state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3))
1913 0 : Found = Count3;
1914 0 : if (state.dataDualDuct->dd_airterminal(Count1).RecircAirInletNodeNum ==
1915 0 : state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3))
1916 0 : Found = Count3;
1917 : }
1918 : }
1919 40 : if (Found != 0) break;
1920 : }
1921 34 : if (Found == 0) ChrName = "**Unknown**";
1922 :
1923 34 : std::string_view damperType = cmoNameArray[static_cast<int>(state.dataDualDuct->dd_airterminal(Count1).DamperType)];
1924 48 : if ((state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::ConstantVolume) ||
1925 14 : (state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::VariableVolume)) {
1926 138 : print(state.files.bnd,
1927 : " Dual Duct Damper,{},{},{},{},{},Hot Air,{}\n",
1928 : Count1,
1929 : damperType,
1930 23 : state.dataDualDuct->dd_airterminal(Count1).Name,
1931 46 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).HotAirInletNodeNum),
1932 46 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
1933 23 : ChrName);
1934 :
1935 138 : print(state.files.bnd,
1936 : " Dual Duct Damper,{},{},{},{},{},Cold Air,{}\n",
1937 : Count1,
1938 : damperType,
1939 23 : state.dataDualDuct->dd_airterminal(Count1).Name,
1940 46 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).ColdAirInletNodeNum),
1941 46 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
1942 23 : ChrName);
1943 :
1944 11 : } else if (state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::OutdoorAir) {
1945 66 : print(state.files.bnd,
1946 : "Dual Duct Damper, {},{},{},{},{},Outdoor Air,{}\n",
1947 : Count1,
1948 : damperType,
1949 11 : state.dataDualDuct->dd_airterminal(Count1).Name,
1950 22 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OAInletNodeNum),
1951 22 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
1952 11 : ChrName);
1953 66 : print(state.files.bnd,
1954 : "Dual Duct Damper, {},{},{},{},{},Recirculated Air,{}\n",
1955 : Count1,
1956 : damperType,
1957 11 : state.dataDualDuct->dd_airterminal(Count1).Name,
1958 22 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).RecircAirInletNodeNum),
1959 22 : state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
1960 11 : ChrName);
1961 : }
1962 : }
1963 : }
1964 :
1965 11 : void GetDualDuctOutdoorAirRecircUse(EnergyPlusData &state,
1966 : [[maybe_unused]] std::string const &CompTypeName,
1967 : std::string_view CompName,
1968 : bool &RecircIsUsed)
1969 : {
1970 :
1971 : // SUBROUTINE INFORMATION:
1972 : // AUTHOR B. Griffith
1973 : // DATE WRITTEN Aug 2011
1974 : // MODIFIED na
1975 : // RE-ENGINEERED na
1976 :
1977 : // PURPOSE OF THIS SUBROUTINE:
1978 : // get routine to learn if a dual duct outdoor air unit is using its recirc deck
1979 :
1980 11 : RecircIsUsed = true;
1981 11 : if (state.dataDualDuct->GetDualDuctOutdoorAirRecircUseFirstTimeOnly) {
1982 1 : state.dataDualDuct->NumDualDuctVarVolOA = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVarVolOA);
1983 1 : state.dataDualDuct->RecircIsUsedARR.allocate(state.dataDualDuct->NumDualDuctVarVolOA);
1984 1 : state.dataDualDuct->DamperNamesARR.allocate(state.dataDualDuct->NumDualDuctVarVolOA);
1985 1 : if (state.dataDualDuct->NumDualDuctVarVolOA > 0) {
1986 2 : Array1D<Real64> NumArray(2, 0.0);
1987 2 : Array1D_string AlphArray(7);
1988 2 : Array1D_string cAlphaFields(7); // Alpha field names
1989 2 : Array1D_string cNumericFields(2); // Numeric field names
1990 2 : Array1D_bool lAlphaBlanks(7, true); // Logical array, alpha field input BLANK = .TRUE.
1991 2 : Array1D_bool lNumericBlanks(2, true); // Logical array, numeric field input BLANK = .TRUE.
1992 12 : for (int DamperIndex = 1; DamperIndex <= state.dataDualDuct->NumDualDuctVarVolOA; ++DamperIndex) {
1993 :
1994 : int NumAlphas;
1995 : int NumNums;
1996 : int IOStat;
1997 11 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1998 : cCMO_DDVarVolOA,
1999 : DamperIndex,
2000 : AlphArray,
2001 : NumAlphas,
2002 : NumArray,
2003 : NumNums,
2004 : IOStat,
2005 : lNumericBlanks,
2006 : lAlphaBlanks,
2007 : cAlphaFields,
2008 : cNumericFields);
2009 11 : state.dataDualDuct->DamperNamesARR(DamperIndex) = AlphArray(1);
2010 11 : if (!lAlphaBlanks(5)) {
2011 6 : state.dataDualDuct->RecircIsUsedARR(DamperIndex) = true;
2012 : } else {
2013 5 : state.dataDualDuct->RecircIsUsedARR(DamperIndex) = false;
2014 : }
2015 : }
2016 : }
2017 1 : state.dataDualDuct->GetDualDuctOutdoorAirRecircUseFirstTimeOnly = false;
2018 : }
2019 :
2020 11 : int DamperIndex = UtilityRoutines::FindItemInList(CompName, state.dataDualDuct->DamperNamesARR, state.dataDualDuct->NumDualDuctVarVolOA);
2021 11 : if (DamperIndex > 0) {
2022 11 : RecircIsUsed = state.dataDualDuct->RecircIsUsedARR(DamperIndex);
2023 : }
2024 11 : }
2025 :
2026 139218 : void DualDuctAirTerminal::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
2027 : {
2028 : // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction, for AirLoopNum > 0 only for now
2029 139218 : if (this->AirLoopNum > 0) {
2030 139195 : this->OutdoorAirFlowRate =
2031 139195 : (this->dd_airterminalOutlet.AirMassFlowRate / state.dataEnvrn->StdRhoAir) * state.dataAirLoop->AirLoopFlow(this->AirLoopNum).OAFrac;
2032 : }
2033 139218 : }
2034 :
2035 : } // namespace DualDuct
2036 :
2037 2313 : } // namespace EnergyPlus
|