LCOV - code coverage report
Current view: top level - EnergyPlus - DualDuct.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 25.8 % 999 258
Test Date: 2025-06-02 12:03:30 Functions: 57.1 % 14 8

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

Generated by: LCOV version 2.0-1