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