LCOV - code coverage report
Current view: top level - EnergyPlus - DualDuct.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 82.6 % 999 825
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 14 14

            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       205994 :     void SimulateDualDuct(
     107              :         EnergyPlusData &state, std::string_view CompName, bool const FirstHVACIteration, int const ZoneNum, int const ZoneNodeNum, int &CompIndex)
     108              :     {
     109              : 
     110              :         // SUBROUTINE INFORMATION:
     111              :         //       AUTHOR         Richard Liesen
     112              :         //       DATE WRITTEN   February 2000
     113              :         //       MODIFIED       na
     114              :         //       RE-ENGINEERED  na
     115              : 
     116              :         // PURPOSE OF THIS SUBROUTINE:
     117              :         // This subroutine manages Damper component simulation.
     118              :         // It is called from the SimAirLoopComponent
     119              :         // at the system time step.
     120              : 
     121              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     122              :         int DDNum; // The Damper that you are currently loading input into
     123              : 
     124              :         // Obtains and Allocates Damper related parameters from input file
     125       205994 :         if (state.dataDualDuct->GetDualDuctInputFlag) { // First time subroutine has been entered
     126            8 :             GetDualDuctInput(state);
     127            8 :             state.dataDualDuct->GetDualDuctInputFlag = false;
     128              :         }
     129              : 
     130              :         // Find the correct DDNumber with the AirLoop & CompNum from AirLoop Derived Type
     131       205994 :         if (CompIndex == 0) {
     132           34 :             DDNum = Util::FindItemInList(CompName, state.dataDualDuct->dd_airterminal, &DualDuctAirTerminal::Name);
     133           34 :             if (DDNum == 0) {
     134            0 :                 ShowFatalError(state, format("SimulateDualDuct: Damper not found={}", CompName));
     135              :             }
     136           34 :             CompIndex = DDNum;
     137              :         } else {
     138       205960 :             DDNum = CompIndex;
     139       205960 :             if (DDNum > state.dataDualDuct->NumDDAirTerminal || DDNum < 1) {
     140            0 :                 ShowFatalError(state,
     141            0 :                                format("SimulateDualDuct: Invalid CompIndex passed={}, Number of Dampers={}, Damper name={}",
     142              :                                       CompIndex,
     143            0 :                                       state.dataDualDuct->NumDDAirTerminal,
     144              :                                       CompName));
     145              :             }
     146       205960 :             if (state.dataDualDuct->dd_airterminal(DDNum).CheckEquipName) {
     147           34 :                 if (CompName != state.dataDualDuct->dd_airterminal(DDNum).Name) {
     148            0 :                     ShowFatalError(state,
     149            0 :                                    format("SimulateDualDuct: Invalid CompIndex passed={}, Damper name={}, stored Damper Name for that index={}",
     150              :                                           CompIndex,
     151              :                                           CompName,
     152            0 :                                           state.dataDualDuct->dd_airterminal(DDNum).Name));
     153              :                 }
     154           34 :                 state.dataDualDuct->dd_airterminal(DDNum).CheckEquipName = false;
     155              :             }
     156              :         }
     157              : 
     158       205994 :         auto &thisDualDuct(state.dataDualDuct->dd_airterminal(DDNum));
     159              : 
     160       205994 :         if (CompIndex > 0) {
     161       205994 :             state.dataSize->CurTermUnitSizingNum = state.dataDefineEquipment->AirDistUnit(thisDualDuct.ADUNum).TermUnitSizingNum;
     162              :             // With the correct DDNum Initialize
     163       205994 :             thisDualDuct.InitDualDuct(state, FirstHVACIteration); // Initialize all Damper related parameters
     164              : 
     165              :             // Calculate the Correct Damper Model with the current DDNum
     166       205994 :             switch (thisDualDuct.DamperType) {
     167       122091 :             case DualDuctDamper::ConstantVolume: { // 'AirTerminal:DualDuct:ConstantVolume'
     168       122091 :                 thisDualDuct.SimDualDuctConstVol(state, ZoneNum, ZoneNodeNum);
     169       122091 :             } break;
     170        16275 :             case DualDuctDamper::VariableVolume: { // 'AirTerminal:DualDuct:VAV'
     171        16275 :                 thisDualDuct.SimDualDuctVarVol(state, ZoneNum, ZoneNodeNum);
     172        16275 :             } break;
     173        67628 :             case DualDuctDamper::OutdoorAir: {
     174        67628 :                 thisDualDuct.SimDualDuctVAVOutdoorAir(state, ZoneNum, ZoneNodeNum); // 'AirTerminal:DualDuct:VAV:OutdoorAir'
     175        67628 :             } break;
     176            0 :             default:
     177            0 :                 break;
     178              :             }
     179              : 
     180              :             // Update the current Damper to the outlet nodes
     181       205994 :             thisDualDuct.UpdateDualDuct(state);
     182              :         } else {
     183            0 :             ShowFatalError(state, format("SimulateDualDuct: Damper not found={}", CompName));
     184              :         }
     185       205994 :     }
     186              : 
     187            8 :     void GetDualDuctInput(EnergyPlusData &state)
     188              :     {
     189              : 
     190              :         // SUBROUTINE INFORMATION:
     191              :         //       AUTHOR         Richard Liesen
     192              :         //       DATE WRITTEN   April 1998
     193              :         //       MODIFIED       Julien Marrec of EffiBEM, 2017-12-18
     194              :         //       RE-ENGINEERED  na
     195              : 
     196              :         // PURPOSE OF THIS SUBROUTINE:
     197              :         // This subroutine is the main routine to call other input routines and Get routines
     198              : 
     199              :         // METHODOLOGY EMPLOYED:
     200              :         // Uses the status flags to trigger events.
     201              : 
     202              :         // SUBROUTINE PARAMETER DEFINITIONS:
     203              :         static constexpr std::string_view RoutineName("GetDualDuctInput: "); // include trailing bla
     204              :         static constexpr std::string_view routineName = "GetDualDuctInput";
     205              : 
     206              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     207              :         int NumAlphas;
     208              :         int NumNums;
     209              :         int IOStat;
     210            8 :         Array1D<Real64> NumArray(2, 0.0);
     211            8 :         Array1D_string AlphArray(7);
     212            8 :         Array1D_string cAlphaFields(7);       // Alpha field names
     213            8 :         Array1D_string cNumericFields(2);     // Numeric field names
     214            8 :         Array1D_bool lAlphaBlanks(7, true);   // Logical array, alpha field input BLANK = .TRUE.
     215            8 :         Array1D_bool lNumericBlanks(2, true); // Logical array, numeric field input BLANK = .TRUE.
     216            8 :         std::string CurrentModuleObject;      // for ease in getting objects
     217            8 :         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            8 :         Real64 DummyOAFlow(0.0);
     221              : 
     222            8 :         int NumDualDuctConstVolDampers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDConstantVolume);
     223            8 :         int NumDualDuctVarVolDampers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVariableVolume);
     224            8 :         state.dataDualDuct->NumDualDuctVarVolOA = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVarVolOA);
     225            8 :         state.dataDualDuct->NumDDAirTerminal = NumDualDuctConstVolDampers + NumDualDuctVarVolDampers + state.dataDualDuct->NumDualDuctVarVolOA;
     226            8 :         state.dataDualDuct->dd_airterminal.allocate(state.dataDualDuct->NumDDAirTerminal);
     227            8 :         state.dataDualDuct->UniqueDualDuctAirTerminalNames.reserve(state.dataDualDuct->NumDDAirTerminal);
     228              : 
     229            8 :         if (NumDualDuctConstVolDampers > 0) {
     230            6 :             CurrentModuleObject = cCMO_DDConstantVolume;
     231           26 :             for (int DamperIndex = 1; DamperIndex <= NumDualDuctConstVolDampers; ++DamperIndex) {
     232              : 
     233              :                 // Load the info from the damper
     234              : 
     235           20 :                 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           20 :                 ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
     249              : 
     250              :                 // Anything below this line in this control block should use DDNum
     251           20 :                 int DDNum = DamperIndex;
     252           20 :                 auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
     253           20 :                 GlobalNames::VerifyUniqueInterObjectName(
     254           40 :                     state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
     255           20 :                 thisDD.Name = AlphArray(1);
     256           20 :                 thisDD.DamperType = DualDuctDamper::ConstantVolume;
     257           20 :                 if (lAlphaBlanks(2)) {
     258            5 :                     thisDD.availSched = Sched::GetScheduleAlwaysOn(state);
     259           15 :                 } 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           60 :                 thisDD.OutletNodeNum = GetOnlySingleNode(state,
     264           20 :                                                          AlphArray(3),
     265              :                                                          ErrorsFound,
     266              :                                                          DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
     267           20 :                                                          thisDD.Name,
     268              :                                                          DataLoopNode::NodeFluidType::Air,
     269              :                                                          DataLoopNode::ConnectionType::Outlet,
     270              :                                                          NodeInputManager::CompFluidStream::Primary,
     271              :                                                          DataLoopNode::ObjectIsNotParent,
     272           20 :                                                          cAlphaFields(3));
     273           60 :                 thisDD.HotAirInletNodeNum = GetOnlySingleNode(state,
     274           20 :                                                               AlphArray(4),
     275              :                                                               ErrorsFound,
     276              :                                                               DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
     277           20 :                                                               thisDD.Name,
     278              :                                                               DataLoopNode::NodeFluidType::Air,
     279              :                                                               DataLoopNode::ConnectionType::Inlet,
     280              :                                                               NodeInputManager::CompFluidStream::Primary,
     281              :                                                               DataLoopNode::ObjectIsNotParent,
     282           20 :                                                               cAlphaFields(4));
     283           60 :                 thisDD.ColdAirInletNodeNum = GetOnlySingleNode(state,
     284           20 :                                                                AlphArray(5),
     285              :                                                                ErrorsFound,
     286              :                                                                DataLoopNode::ConnectionObjectType::AirTerminalDualDuctConstantVolume,
     287           20 :                                                                thisDD.Name,
     288              :                                                                DataLoopNode::NodeFluidType::Air,
     289              :                                                                DataLoopNode::ConnectionType::Inlet,
     290              :                                                                NodeInputManager::CompFluidStream::Primary,
     291              :                                                                DataLoopNode::ObjectIsNotParent,
     292           20 :                                                                cAlphaFields(5));
     293              : 
     294           20 :                 thisDD.MaxAirVolFlowRate = NumArray(1);
     295           20 :                 thisDD.ZoneMinAirFracDes = 0.0;
     296              : 
     297              :                 // Register component set data - one for heat and one for cool
     298           40 :                 BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":HEAT", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
     299           20 :                 BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":COOL", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
     300              : 
     301           90 :                 for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     302           70 :                     if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
     303           20 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.ColdAirInletNodeNum;
     304           20 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.HotAirInletNodeNum;
     305           20 :                         thisDD.ADUNum = ADUNum;
     306              :                     }
     307              :                 }
     308              :                 // one assumes if there isn't one assigned, it's an error?
     309           20 :                 if (thisDD.ADUNum == 0) {
     310            0 :                     auto &thisObjType = damperTypeStrings[static_cast<int>(thisDD.DamperType)];
     311            0 :                     ShowSevereError(
     312              :                         state,
     313            0 :                         format("{}No matching List:Zone:AirTerminal for AirTerminal:DualDuct = [{},{}].", RoutineName, thisObjType, thisDD.Name));
     314            0 :                     ShowContinueError(state, 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           95 :                     for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     320           75 :                         auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
     321           75 :                         if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     322            5 :                             continue;
     323              :                         }
     324          140 :                         for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
     325           70 :                             if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
     326           20 :                                 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           20 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.ColdAirInletNodeNum;
     335           20 :                                     thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.HotAirInletNodeNum;
     336           20 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
     337           20 :                                     thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
     338           20 :                                     state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
     339           20 :                                         thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
     340           20 :                                     state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
     341              :                                 }
     342           20 :                                 thisDD.CtrlZoneNum = CtrlZone;
     343           20 :                                 thisDD.CtrlZoneInNodeIndex = SupAirIn;
     344              :                             }
     345              :                         }
     346              :                     }
     347              :                 }
     348              :                 // Setup the Average damper Position output variable
     349              :                 // CurrentModuleObject='AirTerminal:DualDuct:ConstantVolume'
     350           40 :                 SetupOutputVariable(state,
     351              :                                     "Zone Air Terminal Cold Supply Duct Damper Position",
     352              :                                     Constant::Units::None,
     353           20 :                                     thisDD.ColdAirDamperPosition,
     354              :                                     OutputProcessor::TimeStepType::System,
     355              :                                     OutputProcessor::StoreType::Average,
     356           20 :                                     thisDD.Name);
     357           40 :                 SetupOutputVariable(state,
     358              :                                     "Zone Air Terminal Hot Supply Duct Damper Position",
     359              :                                     Constant::Units::None,
     360           20 :                                     thisDD.HotAirDamperPosition,
     361              :                                     OutputProcessor::TimeStepType::System,
     362              :                                     OutputProcessor::StoreType::Average,
     363           20 :                                     thisDD.Name);
     364              : 
     365              :             } // end Number of Damper Loop
     366              :         }
     367              : 
     368            8 :         if (NumDualDuctVarVolDampers > 0) {
     369            1 :             CurrentModuleObject = cCMO_DDVariableVolume;
     370            4 :             for (int DamperIndex = 1; DamperIndex <= NumDualDuctVarVolDampers; ++DamperIndex) {
     371              : 
     372              :                 // Load the info from the damper
     373              : 
     374            3 :                 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            3 :                 ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
     388              : 
     389              :                 // Anything below this line in this control block should use DDNum
     390            3 :                 int DDNum = DamperIndex + NumDualDuctConstVolDampers;
     391            3 :                 auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
     392            3 :                 GlobalNames::VerifyUniqueInterObjectName(
     393            6 :                     state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
     394            3 :                 thisDD.Name = AlphArray(1);
     395            3 :                 thisDD.DamperType = DualDuctDamper::VariableVolume;
     396            3 :                 if (lAlphaBlanks(2)) {
     397            0 :                     thisDD.availSched = Sched::GetScheduleAlwaysOn(state);
     398            3 :                 } 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            9 :                 thisDD.OutletNodeNum = GetOnlySingleNode(state,
     403            3 :                                                          AlphArray(3),
     404              :                                                          ErrorsFound,
     405              :                                                          DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
     406            3 :                                                          thisDD.Name,
     407              :                                                          DataLoopNode::NodeFluidType::Air,
     408              :                                                          DataLoopNode::ConnectionType::Outlet,
     409              :                                                          NodeInputManager::CompFluidStream::Primary,
     410              :                                                          DataLoopNode::ObjectIsNotParent,
     411            3 :                                                          cAlphaFields(3));
     412            9 :                 thisDD.HotAirInletNodeNum = GetOnlySingleNode(state,
     413            3 :                                                               AlphArray(4),
     414              :                                                               ErrorsFound,
     415              :                                                               DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
     416            3 :                                                               thisDD.Name,
     417              :                                                               DataLoopNode::NodeFluidType::Air,
     418              :                                                               DataLoopNode::ConnectionType::Inlet,
     419              :                                                               NodeInputManager::CompFluidStream::Primary,
     420              :                                                               DataLoopNode::ObjectIsNotParent,
     421            3 :                                                               cAlphaFields(4));
     422            9 :                 thisDD.ColdAirInletNodeNum = GetOnlySingleNode(state,
     423            3 :                                                                AlphArray(5),
     424              :                                                                ErrorsFound,
     425              :                                                                DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAV,
     426            3 :                                                                thisDD.Name,
     427              :                                                                DataLoopNode::NodeFluidType::Air,
     428              :                                                                DataLoopNode::ConnectionType::Inlet,
     429              :                                                                NodeInputManager::CompFluidStream::Primary,
     430              :                                                                DataLoopNode::ObjectIsNotParent,
     431            3 :                                                                cAlphaFields(5));
     432              : 
     433            3 :                 thisDD.MaxAirVolFlowRate = NumArray(1);
     434            3 :                 thisDD.ZoneMinAirFracDes = NumArray(2);
     435              : 
     436              :                 // Register component set data - one for heat and one for cool
     437            6 :                 BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":HEAT", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
     438            3 :                 BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":COOL", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
     439              : 
     440           12 :                 for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     441            9 :                     if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
     442            3 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.ColdAirInletNodeNum;
     443            3 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.HotAirInletNodeNum;
     444            3 :                         thisDD.ADUNum = ADUNum;
     445              :                     }
     446              :                 }
     447              :                 // one assumes if there isn't one assigned, it's an error?
     448            3 :                 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           12 :                     for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     459            9 :                         auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
     460            9 :                         if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     461            0 :                             continue;
     462              :                         }
     463           18 :                         for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
     464            9 :                             if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
     465            3 :                                 thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.ColdAirInletNodeNum;
     466            3 :                                 thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.HotAirInletNodeNum;
     467            3 :                                 thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
     468            3 :                                 thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
     469            3 :                                 state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
     470            3 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
     471            3 :                                 state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
     472              : 
     473            3 :                                 thisDD.CtrlZoneNum = CtrlZone;
     474            3 :                                 thisDD.CtrlZoneInNodeIndex = SupAirIn;
     475              :                             }
     476              :                         }
     477              :                     }
     478              :                 }
     479            3 :                 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            3 :                 if (lAlphaBlanks(7)) {
     491            3 :                     thisDD.ZoneTurndownMinAirFrac = 1.0;
     492            0 :                 } 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            6 :                 SetupOutputVariable(state,
     500              :                                     "Zone Air Terminal Cold Supply Duct Damper Position",
     501              :                                     Constant::Units::None,
     502            3 :                                     thisDD.ColdAirDamperPosition,
     503              :                                     OutputProcessor::TimeStepType::System,
     504              :                                     OutputProcessor::StoreType::Average,
     505            3 :                                     thisDD.Name);
     506            6 :                 SetupOutputVariable(state,
     507              :                                     "Zone Air Terminal Hot Supply Duct Damper Position",
     508              :                                     Constant::Units::None,
     509            3 :                                     thisDD.HotAirDamperPosition,
     510              :                                     OutputProcessor::TimeStepType::System,
     511              :                                     OutputProcessor::StoreType::Average,
     512            3 :                                     thisDD.Name);
     513            6 :                 SetupOutputVariable(state,
     514              :                                     "Zone Air Terminal Outdoor Air Volume Flow Rate",
     515              :                                     Constant::Units::m3_s,
     516            3 :                                     thisDD.OutdoorAirFlowRate,
     517              :                                     OutputProcessor::TimeStepType::System,
     518              :                                     OutputProcessor::StoreType::Average,
     519            3 :                                     thisDD.Name);
     520              :             } // end Number of Damper Loop
     521              :         }
     522              : 
     523            8 :         if (state.dataDualDuct->NumDualDuctVarVolOA > 0) {
     524            1 :             CurrentModuleObject = cCMO_DDVarVolOA;
     525           12 :             for (int DamperIndex = 1; DamperIndex <= state.dataDualDuct->NumDualDuctVarVolOA; ++DamperIndex) {
     526              : 
     527              :                 // Load the info from the damper
     528           11 :                 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           11 :                 ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
     542              : 
     543              :                 // Anything below this line in this control block should use DDNum
     544           11 :                 int DDNum = DamperIndex + NumDualDuctConstVolDampers + NumDualDuctVarVolDampers;
     545           11 :                 auto &thisDD = state.dataDualDuct->dd_airterminal(DDNum);
     546           11 :                 GlobalNames::VerifyUniqueInterObjectName(
     547           22 :                     state, state.dataDualDuct->UniqueDualDuctAirTerminalNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
     548           11 :                 thisDD.Name = AlphArray(1);
     549           11 :                 thisDD.DamperType = DualDuctDamper::OutdoorAir;
     550           11 :                 if (lAlphaBlanks(2)) {
     551            0 :                     thisDD.availSched = Sched::GetScheduleAlwaysOn(state);
     552           11 :                 } 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           33 :                 thisDD.OutletNodeNum = GetOnlySingleNode(state,
     557           11 :                                                          AlphArray(3),
     558              :                                                          ErrorsFound,
     559              :                                                          DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
     560           11 :                                                          thisDD.Name,
     561              :                                                          DataLoopNode::NodeFluidType::Air,
     562              :                                                          DataLoopNode::ConnectionType::Outlet,
     563              :                                                          NodeInputManager::CompFluidStream::Primary,
     564              :                                                          DataLoopNode::ObjectIsNotParent,
     565           11 :                                                          cAlphaFields(3));
     566           33 :                 thisDD.OAInletNodeNum = GetOnlySingleNode(state,
     567           11 :                                                           AlphArray(4),
     568              :                                                           ErrorsFound,
     569              :                                                           DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
     570           11 :                                                           thisDD.Name,
     571              :                                                           DataLoopNode::NodeFluidType::Air,
     572              :                                                           DataLoopNode::ConnectionType::Inlet,
     573              :                                                           NodeInputManager::CompFluidStream::Primary,
     574              :                                                           DataLoopNode::ObjectIsNotParent,
     575           11 :                                                           cAlphaFields(4));
     576              : 
     577           11 :                 if (!lAlphaBlanks(5)) {
     578            6 :                     thisDD.RecircAirInletNodeNum = GetOnlySingleNode(state,
     579            6 :                                                                      AlphArray(5),
     580              :                                                                      ErrorsFound,
     581              :                                                                      DataLoopNode::ConnectionObjectType::AirTerminalDualDuctVAVOutdoorAir,
     582            6 :                                                                      thisDD.Name,
     583              :                                                                      DataLoopNode::NodeFluidType::Air,
     584              :                                                                      DataLoopNode::ConnectionType::Inlet,
     585              :                                                                      NodeInputManager::CompFluidStream::Primary,
     586              :                                                                      DataLoopNode::ObjectIsNotParent,
     587            6 :                                                                      cAlphaFields(5));
     588              :                 } else {
     589              :                     // for this model, we intentionally allow not using the recirc side
     590            5 :                     thisDD.RecircIsUsed = false;
     591              :                 }
     592              : 
     593           11 :                 thisDD.MaxAirVolFlowRate = NumArray(1);
     594           11 :                 thisDD.MaxAirMassFlowRate = thisDD.MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
     595              : 
     596              :                 // Register component set data - one for OA and one for RA
     597           11 :                 BranchNodeConnections::TestCompSet(state, CurrentModuleObject + ":OutdoorAir", thisDD.Name, AlphArray(4), AlphArray(3), "Air Nodes");
     598           11 :                 if (thisDD.RecircIsUsed) {
     599           18 :                     BranchNodeConnections::TestCompSet(
     600           12 :                         state, CurrentModuleObject + ":RecirculatedAir", thisDD.Name, AlphArray(5), AlphArray(3), "Air Nodes");
     601              :                 }
     602              : 
     603           11 :                 thisDD.OAPerPersonMode = static_cast<PerPersonMode>(getEnumValue(modeStrings, AlphArray(7)));
     604           11 :                 if (thisDD.OAPerPersonMode == PerPersonMode::Invalid) {
     605            4 :                     thisDD.OAPerPersonMode = PerPersonMode::ModeNotSet;
     606              :                 }
     607              :                 // checks on this are done later
     608              : 
     609          132 :                 for (ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     610          121 :                     if (thisDD.OutletNodeNum == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
     611           11 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisDD.OAInletNodeNum;
     612           11 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum2 = thisDD.RecircAirInletNodeNum;
     613           11 :                         thisDD.ADUNum = ADUNum;
     614              :                     }
     615              :                 }
     616              :                 // one assumes if there isn't one assigned, it's an error?
     617           11 :                 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          132 :                     for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     628          121 :                         auto &thisZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(CtrlZone);
     629          121 :                         if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     630            0 :                             continue;
     631              :                         }
     632          297 :                         for (SupAirIn = 1; SupAirIn <= thisZoneEquipConfig.NumInletNodes; ++SupAirIn) {
     633          176 :                             if (thisDD.OutletNodeNum == thisZoneEquipConfig.InletNode(SupAirIn)) {
     634           11 :                                 if (thisDD.RecircIsUsed) {
     635            6 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.RecircAirInletNodeNum;
     636              :                                 } else {
     637            5 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).InNode = thisDD.OAInletNodeNum;
     638              :                                 }
     639           11 :                                 thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).InNode = thisDD.OAInletNodeNum;
     640           11 :                                 thisZoneEquipConfig.AirDistUnitCool(SupAirIn).OutNode = thisDD.OutletNodeNum;
     641           11 :                                 thisZoneEquipConfig.AirDistUnitHeat(SupAirIn).OutNode = thisDD.OutletNodeNum;
     642           11 :                                 state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).TermUnitSizingNum =
     643           11 :                                     thisZoneEquipConfig.AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
     644           11 :                                 state.dataDefineEquipment->AirDistUnit(thisDD.ADUNum).ZoneEqNum = CtrlZone;
     645              : 
     646           11 :                                 thisDD.CtrlZoneNum = CtrlZone;
     647           11 :                                 thisDD.CtrlZoneInNodeIndex = SupAirIn;
     648              :                             }
     649              :                         }
     650              :                     }
     651              :                 }
     652           11 :                 thisDD.OARequirementsPtr = Util::FindItemInList(AlphArray(6), state.dataSize->OARequirements);
     653           11 :                 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           11 :                     thisDD.NoOAFlowInputFromUser = false;
     659              : 
     660              :                     // now fill design OA rate
     661           11 :                     thisDD.CalcOAOnlyMassFlow(state, DummyOAFlow, thisDD.DesignOAFlowRate);
     662              : 
     663           11 :                     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           11 :                 if (thisDD.OAPerPersonMode == PerPersonMode::ModeNotSet) {
     687            4 :                     DummyOAFlow = state.dataSize->OARequirements(thisDD.OARequirementsPtr).OAFlowPerPerson;
     688            4 :                     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           22 :                 SetupOutputVariable(state,
     705              :                                     "Zone Air Terminal Outdoor Air Duct Damper Position",
     706              :                                     Constant::Units::None,
     707           11 :                                     thisDD.OADamperPosition,
     708              :                                     OutputProcessor::TimeStepType::System,
     709              :                                     OutputProcessor::StoreType::Average,
     710           11 :                                     thisDD.Name);
     711           22 :                 SetupOutputVariable(state,
     712              :                                     "Zone Air Terminal Recirculated Air Duct Damper Position",
     713              :                                     Constant::Units::None,
     714           11 :                                     thisDD.RecircAirDamperPosition,
     715              :                                     OutputProcessor::TimeStepType::System,
     716              :                                     OutputProcessor::StoreType::Average,
     717           11 :                                     thisDD.Name);
     718           22 :                 SetupOutputVariable(state,
     719              :                                     "Zone Air Terminal Outdoor Air Fraction",
     720              :                                     Constant::Units::None,
     721           11 :                                     thisDD.OAFraction,
     722              :                                     OutputProcessor::TimeStepType::System,
     723              :                                     OutputProcessor::StoreType::Average,
     724           11 :                                     thisDD.Name);
     725              : 
     726              :             } // end Number of Damper Loop
     727              :         }
     728              : 
     729            8 :         if (ErrorsFound) {
     730            0 :             ShowFatalError(state, format("{}Errors found in input.  Preceding condition(s) cause termination.", RoutineName));
     731              :         }
     732            8 :     }
     733              : 
     734       205994 :     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       205994 :         if (!state.dataDualDuct->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
     759            8 :             state.dataDualDuct->ZoneEquipmentListChecked = true;
     760              :             // Check to see if there is a Air Distribution Unit on the Zone Equipment List
     761           42 :             for (Loop = 1; Loop <= state.dataDualDuct->NumDDAirTerminal; ++Loop) {
     762           34 :                 if (this->ADUNum == 0) {
     763            0 :                     continue;
     764              :                 }
     765           68 :                 if (DataZoneEquipment::CheckZoneEquipmentList(
     766           34 :                         state, "ZONEHVAC:AIRDISTRIBUTIONUNIT", state.dataDefineEquipment->AirDistUnit(this->ADUNum).Name)) {
     767           34 :                     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       205994 :         if (!state.dataGlobal->SysSizingCalc && this->MySizeFlag) {
     785           34 :             this->SizeDualDuct(state);
     786           34 :             this->MySizeFlag = false;
     787              :         }
     788              : 
     789              :         // Do the Begin Environment initializations
     790       205994 :         if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag) {
     791              : 
     792          238 :             if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
     793          150 :                 OutNode = this->OutletNodeNum;
     794          150 :                 HotInNode = this->HotAirInletNodeNum;
     795          150 :                 ColdInNode = this->ColdAirInletNodeNum;
     796          150 :                 state.dataLoopNodes->Node(OutNode).MassFlowRateMax = this->MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
     797          150 :                 if (this->DamperType == DualDuctDamper::ConstantVolume) {
     798          132 :                     state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
     799           18 :                 } else if (this->DamperType == DualDuctDamper::VariableVolume) {
     800              :                     // get dual duct air terminal box minimum flow fraction value
     801           18 :                     if (this->zoneTurndownMinAirFracSched != nullptr) {
     802            0 :                         this->ZoneTurndownMinAirFrac = this->zoneTurndownMinAirFracSched->getMinVal(state);
     803              :                     } else {
     804           18 :                         this->ZoneTurndownMinAirFrac = 1.0;
     805              :                     }
     806           18 :                     state.dataLoopNodes->Node(OutNode).MassFlowRateMin =
     807           18 :                         state.dataLoopNodes->Node(OutNode).MassFlowRateMax * this->ZoneMinAirFracDes * this->ZoneTurndownMinAirFrac;
     808              :                 } else {
     809            0 :                     state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
     810              :                 }
     811          150 :                 this->dd_airterminalHotAirInlet.AirMassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
     812          150 :                 this->dd_airterminalColdAirInlet.AirMassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
     813          150 :                 state.dataLoopNodes->Node(HotInNode).MassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
     814          150 :                 state.dataLoopNodes->Node(ColdInNode).MassFlowRateMax = state.dataLoopNodes->Node(OutNode).MassFlowRateMax;
     815          150 :                 state.dataLoopNodes->Node(HotInNode).MassFlowRateMin = 0.0;
     816          150 :                 state.dataLoopNodes->Node(ColdInNode).MassFlowRateMin = 0.0;
     817          150 :                 this->MyEnvrnFlag = false;
     818              : 
     819           88 :             } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
     820              :                 // Initialize for DualDuct:VAV:OutdoorAir
     821           88 :                 OutNode = this->OutletNodeNum;
     822           88 :                 OAInNode = this->OAInletNodeNum;
     823           88 :                 if (this->RecircIsUsed) {
     824           48 :                     RAInNode = this->RecircAirInletNodeNum;
     825              :                 }
     826           88 :                 state.dataLoopNodes->Node(OutNode).MassFlowRateMax = this->MaxAirMassFlowRate;
     827           88 :                 state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
     828           88 :                 this->dd_airterminalOAInlet.AirMassFlowRateMax = this->DesignOAFlowRate * state.dataEnvrn->StdRhoAir;
     829           88 :                 if (this->RecircIsUsed) {
     830           48 :                     this->dd_airterminalRecircAirInlet.AirMassFlowRateMax = this->MaxAirMassFlowRate - this->dd_airterminalOAInlet.AirMassFlowRateMax;
     831           48 :                     state.dataLoopNodes->Node(RAInNode).MassFlowRateMax = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
     832           48 :                     state.dataLoopNodes->Node(RAInNode).MassFlowRateMin = 0.0;
     833           48 :                     this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag = 1.0e-10 * this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
     834              :                 }
     835           88 :                 state.dataLoopNodes->Node(OAInNode).MassFlowRateMax = this->dd_airterminalOAInlet.AirMassFlowRateMax;
     836           88 :                 state.dataLoopNodes->Node(OAInNode).MassFlowRateMin = 0.0;
     837              :                 // figure per person by design level for the OA duct.
     838           88 :                 PeopleFlow = 0.0;
     839          616 :                 for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
     840          528 :                     if (state.dataHeatBal->People(Loop).ZonePtr != this->CtrlZoneNum) {
     841          480 :                         continue;
     842              :                     }
     843           48 :                     DataSizing::OAFlowCalcMethod damperOAFlowMethod = state.dataSize->OARequirements(this->OARequirementsPtr).OAFlowMethod;
     844           48 :                     if (damperOAFlowMethod == DataSizing::OAFlowCalcMethod::PerPerson || damperOAFlowMethod == DataSizing::OAFlowCalcMethod::Sum ||
     845              :                         damperOAFlowMethod == DataSizing::OAFlowCalcMethod::Max) {
     846           48 :                         PeopleFlow +=
     847           48 :                             state.dataHeatBal->People(Loop).NumberOfPeople * state.dataSize->OARequirements(this->OARequirementsPtr).OAFlowPerPerson;
     848              :                     }
     849              :                 }
     850           88 :                 this->MyEnvrnFlag = false;
     851              :             }
     852              :         }
     853              : 
     854       205994 :         if (!state.dataGlobal->BeginEnvrnFlag) {
     855       204114 :             this->MyEnvrnFlag = true;
     856              :         }
     857              : 
     858              :         // Find air loop associated with this terminal unit
     859       205994 :         if (this->MyAirLoopFlag) {
     860          102 :             if (this->AirLoopNum == 0) {
     861           68 :                 if ((this->CtrlZoneNum > 0) && (this->CtrlZoneInNodeIndex > 0)) {
     862           68 :                     this->AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->CtrlZoneNum).InletNodeAirLoopNum(this->CtrlZoneInNodeIndex);
     863           68 :                     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           34 :                 this->MyAirLoopFlag = false;
     868              :             }
     869              :         }
     870              : 
     871              :         // Initialize the Inlet Nodes of the Sys
     872       205994 :         if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
     873       138366 :             HotInNode = this->HotAirInletNodeNum;
     874       138366 :             ColdInNode = this->ColdAirInletNodeNum;
     875       138366 :             OutNode = this->OutletNodeNum;
     876        67628 :         } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
     877        67628 :             OAInNode = this->OAInletNodeNum;
     878        67628 :             if (this->RecircIsUsed) {
     879        36888 :                 RAInNode = this->RecircAirInletNodeNum;
     880              :             }
     881        67628 :             OutNode = this->OutletNodeNum;
     882              :         }
     883              : 
     884       205994 :         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        98419 :             if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
     889        64682 :                 auto &thisHotInNode = state.dataLoopNodes->Node(HotInNode);
     890        64682 :                 auto &thisColdInNode = state.dataLoopNodes->Node(ColdInNode);
     891        64682 :                 Real64 schedValue = this->availSched->getCurrentVal();
     892        64682 :                 if ((thisHotInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     893        49694 :                     thisHotInNode.MassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
     894              :                 } else {
     895        14988 :                     thisHotInNode.MassFlowRate = 0.0;
     896              :                 }
     897        64682 :                 if ((thisColdInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     898        49689 :                     thisColdInNode.MassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
     899              :                 } else {
     900        14993 :                     thisColdInNode.MassFlowRate = 0.0;
     901              :                 }
     902              :                 // Next take care of the Max Avail Flow Rates
     903        64682 :                 if ((thisHotInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
     904        49694 :                     thisHotInNode.MassFlowRateMaxAvail = this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
     905              :                 } else {
     906        14988 :                     thisHotInNode.MassFlowRateMaxAvail = 0.0;
     907              :                 }
     908        64682 :                 if ((thisColdInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
     909        49689 :                     thisColdInNode.MassFlowRateMaxAvail = this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
     910              :                 } else {
     911        14993 :                     thisColdInNode.MassFlowRateMaxAvail = 0.0;
     912              :                 }
     913              :                 // get current time step air terminal box turndown minimum flow fraction
     914        64682 :                 if (this->zoneTurndownMinAirFracSched != nullptr) {
     915            0 :                     this->ZoneTurndownMinAirFrac = this->zoneTurndownMinAirFracSched->getCurrentVal();
     916              :                 } else {
     917        64682 :                     this->ZoneTurndownMinAirFrac = 1.0;
     918              :                 }
     919              :                 // update to the current dual duct minimum air flow fraction
     920        64682 :                 this->ZoneMinAirFrac = this->ZoneMinAirFracDes * this->ZoneTurndownMinAirFrac;
     921              :                 // The last item is to take care of the Min Avail Flow Rates
     922        64682 :                 if ((thisHotInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     923        49694 :                     thisHotInNode.MassFlowRateMinAvail = this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
     924              :                 } else {
     925        14988 :                     thisHotInNode.MassFlowRateMinAvail = 0.0;
     926              :                 }
     927        64682 :                 if ((thisColdInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     928        49689 :                     thisColdInNode.MassFlowRateMinAvail = this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
     929              :                 } else {
     930        14993 :                     thisColdInNode.MassFlowRateMinAvail = 0.0;
     931              :                 }
     932              : 
     933        98419 :             } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
     934        33737 :                 auto &thisOAInNode = state.dataLoopNodes->Node(OAInNode);
     935        33737 :                 Real64 schedValue = this->availSched->getCurrentVal();
     936              :                 // The first time through set the mass flow rate to the Max for VAV:OutdoorAir
     937        33737 :                 if ((thisOAInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     938        33630 :                     thisOAInNode.MassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRateMax;
     939              :                 } else {
     940          107 :                     thisOAInNode.MassFlowRate = 0.0;
     941              :                 }
     942        33737 :                 if (this->RecircIsUsed) {
     943        18402 :                     auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
     944        18402 :                     if ((thisRAInNode.MassFlowRate > 0.0) && (schedValue > 0.0)) {
     945        18354 :                         thisRAInNode.MassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
     946              :                     } else {
     947           48 :                         thisRAInNode.MassFlowRate = 0.0;
     948              :                     }
     949              :                     // clear flow history
     950        18402 :                     this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1 = 0.0;
     951        18402 :                     this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2 = 0.0;
     952        18402 :                     this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3 = 0.0;
     953              :                 }
     954              :                 // Next take care of the Max Avail Flow Rates
     955        33737 :                 if ((thisOAInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
     956        33630 :                     thisOAInNode.MassFlowRateMaxAvail = this->dd_airterminalOAInlet.AirMassFlowRateMax;
     957              :                 } else {
     958          107 :                     thisOAInNode.MassFlowRateMaxAvail = 0.0;
     959              :                 }
     960        33737 :                 if (this->RecircIsUsed) {
     961        18402 :                     auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
     962        18402 :                     if ((thisRAInNode.MassFlowRateMaxAvail > 0.0) && (schedValue > 0.0)) {
     963        18354 :                         thisRAInNode.MassFlowRateMaxAvail = this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
     964              :                     } else {
     965           48 :                         thisRAInNode.MassFlowRateMaxAvail = 0.0;
     966              :                     }
     967              :                 }
     968              :                 // The last item is to take care of the Min Avail Flow Rates. VAV:OutdoorAir
     969        33737 :                 thisOAInNode.MassFlowRateMinAvail = 0.0;
     970        33737 :                 if (this->RecircIsUsed) {
     971        18402 :                     auto &thisRAInNode = state.dataLoopNodes->Node(RAInNode);
     972        18402 :                     thisRAInNode.MassFlowRateMinAvail = 0.0;
     973              :                 }
     974              :             }
     975              :         }
     976              : 
     977              :         // Initialize the Inlet Nodes of the Dampers for Const. Vol and VAV
     978       205994 :         if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
     979              : 
     980       138366 :             this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail =
     981       138366 :                 min(state.dataLoopNodes->Node(OutNode).MassFlowRateMax, state.dataLoopNodes->Node(HotInNode).MassFlowRateMaxAvail);
     982       138366 :             this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail =
     983       138366 :                 min(max(state.dataLoopNodes->Node(OutNode).MassFlowRateMin, state.dataLoopNodes->Node(HotInNode).MassFlowRateMinAvail),
     984       138366 :                     state.dataLoopNodes->Node(HotInNode).MassFlowRateMaxAvail);
     985              : 
     986       138366 :             this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail =
     987       138366 :                 min(state.dataLoopNodes->Node(OutNode).MassFlowRateMax, state.dataLoopNodes->Node(ColdInNode).MassFlowRateMaxAvail);
     988       138366 :             this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail =
     989       138366 :                 min(max(state.dataLoopNodes->Node(OutNode).MassFlowRateMin, state.dataLoopNodes->Node(ColdInNode).MassFlowRateMinAvail),
     990       138366 :                     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       138366 :             this->dd_airterminalHotAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(HotInNode).MassFlowRate;
     996       138366 :             this->dd_airterminalHotAirInlet.AirTemp = state.dataLoopNodes->Node(HotInNode).Temp;
     997       138366 :             this->dd_airterminalHotAirInlet.AirHumRat = state.dataLoopNodes->Node(HotInNode).HumRat;
     998       138366 :             this->dd_airterminalHotAirInlet.AirEnthalpy = state.dataLoopNodes->Node(HotInNode).Enthalpy;
     999       138366 :             this->dd_airterminalColdAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(ColdInNode).MassFlowRate;
    1000       138366 :             this->dd_airterminalColdAirInlet.AirTemp = state.dataLoopNodes->Node(ColdInNode).Temp;
    1001       138366 :             this->dd_airterminalColdAirInlet.AirHumRat = state.dataLoopNodes->Node(ColdInNode).HumRat;
    1002       138366 :             this->dd_airterminalColdAirInlet.AirEnthalpy = state.dataLoopNodes->Node(ColdInNode).Enthalpy;
    1003              : 
    1004              :             // Initialize the Inlet Nodes of the Dampers for VAV:OutdoorAir
    1005        67628 :         } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
    1006        67628 :             this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail = state.dataLoopNodes->Node(OAInNode).MassFlowRateMaxAvail;
    1007        67628 :             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        67628 :             this->dd_airterminalOAInlet.AirMassFlowRate = state.dataLoopNodes->Node(OAInNode).MassFlowRate;
    1013        67628 :             this->dd_airterminalOAInlet.AirTemp = state.dataLoopNodes->Node(OAInNode).Temp;
    1014        67628 :             this->dd_airterminalOAInlet.AirHumRat = state.dataLoopNodes->Node(OAInNode).HumRat;
    1015        67628 :             this->dd_airterminalOAInlet.AirEnthalpy = state.dataLoopNodes->Node(OAInNode).Enthalpy;
    1016        67628 :             if (this->RecircIsUsed) {
    1017        36888 :                 this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail = state.dataLoopNodes->Node(RAInNode).MassFlowRateMaxAvail;
    1018        36888 :                 this->dd_airterminalRecircAirInlet.AirMassFlowRateMinAvail = state.dataLoopNodes->Node(RAInNode).MassFlowRateMinAvail;
    1019        36888 :                 this->dd_airterminalRecircAirInlet.AirMassFlowRate = state.dataLoopNodes->Node(RAInNode).MassFlowRate;
    1020        36888 :                 this->dd_airterminalRecircAirInlet.AirTemp = state.dataLoopNodes->Node(RAInNode).Temp;
    1021        36888 :                 this->dd_airterminalRecircAirInlet.AirHumRat = state.dataLoopNodes->Node(RAInNode).HumRat;
    1022        36888 :                 this->dd_airterminalRecircAirInlet.AirEnthalpy = state.dataLoopNodes->Node(RAInNode).Enthalpy;
    1023              :             }
    1024              :         }
    1025       205994 :     }
    1026              : 
    1027           34 :     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           34 :         if (this->MaxAirVolFlowRate == DataSizing::AutoSize) {
    1044              : 
    1045           19 :             if ((state.dataSize->CurZoneEqNum > 0) && (state.dataSize->CurTermUnitSizingNum > 0)) {
    1046           19 :                 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           19 :                 std::string damperTypeAsString = std::string(damperType);
    1052           19 :                 CheckZoneSizing(state, damperTypeAsString, this->Name);
    1053           19 :                 this->MaxAirVolFlowRate = max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
    1054           19 :                                               state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
    1055           19 :                 if (this->DamperType == DualDuctDamper::OutdoorAir) {
    1056           11 :                     if (this->RecircIsUsed) {
    1057            6 :                         this->DesignRecircFlowRate =
    1058            6 :                             max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
    1059            6 :                                 state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
    1060            6 :                         this->MaxAirVolFlowRate = this->DesignRecircFlowRate + this->DesignOAFlowRate;
    1061              :                     } else {
    1062            5 :                         this->MaxAirVolFlowRate = this->DesignOAFlowRate;
    1063            5 :                         this->DesignRecircFlowRate = 0.0;
    1064              :                     }
    1065           11 :                     this->MaxAirMassFlowRate = this->MaxAirVolFlowRate * state.dataEnvrn->StdRhoAir;
    1066              :                 }
    1067              : 
    1068           19 :                 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           19 :                 BaseSizer::reportSizerOutput(state, damperTypeAsString, this->Name, "Maximum Air Flow Rate [m3/s]", this->MaxAirVolFlowRate);
    1075           19 :                 if (this->DamperType == DualDuctDamper::OutdoorAir) {
    1076           11 :                     BaseSizer::reportSizerOutput(
    1077              :                         state, damperTypeAsString, this->Name, "Maximum Outdoor Air Flow Rate [m3/s]", this->DesignOAFlowRate);
    1078           11 :                     if (this->RecircIsUsed) {
    1079            6 :                         BaseSizer::reportSizerOutput(
    1080              :                             state, damperTypeAsString, this->Name, "Maximum Recirculated Air Flow Rate [m3/s]", this->DesignRecircFlowRate);
    1081              :                     }
    1082              :                 }
    1083           19 :             }
    1084              :         }
    1085           34 :     }
    1086              : 
    1087       122091 :     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       122091 :         QTotLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
    1121              :         // Need the design MassFlowRate for calculations
    1122       122091 :         if (this->availSched->getCurrentVal() > 0.0) {
    1123        98346 :             MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail / 2.0 + this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail / 2.0;
    1124              :         } else {
    1125        23745 :             MassFlow = 0.0;
    1126              :         }
    1127              :         // If there is massflow then need to provide the correct amount of total
    1128              :         //  required zone energy
    1129       122091 :         if (MassFlow > HVAC::SmallMassFlow) {
    1130        98199 :             CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    1131        98199 :             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        98199 :             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        97406 :                 CpAirSysHot = CpAirZn;
    1139        97406 :                 CpAirSysCold = CpAirZn;
    1140              :                 // Determine the Cold Air Mass Flow Rate
    1141        97406 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate =
    1142        97406 :                     (QZnReq - MassFlow * CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) /
    1143        97406 :                     (CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp - CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp);
    1144          793 :             } else if ((QTotLoad > 0.0) && (this->dd_airterminalHotAirInlet.AirMassFlowRate > 0.0)) {
    1145           15 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1146              :             } else {
    1147          778 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
    1148              :             }
    1149              :             // Check to make sure that the calculated flow is not greater than the available flows
    1150        98199 :             if (this->dd_airterminalColdAirInlet.AirMassFlowRate > this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
    1151         3927 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
    1152        94272 :             } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail) {
    1153         9093 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail;
    1154              :             }
    1155              :             // Using Mass Continuity to determine the other duct flow quantity
    1156        98199 :             this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow - this->dd_airterminalColdAirInlet.AirMassFlowRate;
    1157        98199 :             if (this->dd_airterminalHotAirInlet.AirMassFlowRate > this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail) {
    1158            0 :                 this->dd_airterminalHotAirInlet.AirMassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail;
    1159        98199 :             } else if (this->dd_airterminalHotAirInlet.AirMassFlowRate < this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail) {
    1160           10 :                 this->dd_airterminalHotAirInlet.AirMassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail;
    1161              :             }
    1162        98199 :             MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRate + this->dd_airterminalHotAirInlet.AirMassFlowRate;
    1163              :         } else {
    1164              :             // System is Off set massflow to 0.0
    1165        23892 :             MassFlow = 0.0;
    1166              :         }
    1167       122091 :         if (MassFlow > HVAC::SmallMassFlow) {
    1168              :             // After flows are calculated then calculate the mixed air flow properties.
    1169        98199 :             HumRat = (this->dd_airterminalHotAirInlet.AirHumRat * this->dd_airterminalHotAirInlet.AirMassFlowRate +
    1170        98199 :                       this->dd_airterminalColdAirInlet.AirHumRat * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
    1171              :                      MassFlow;
    1172        98199 :             Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy * this->dd_airterminalHotAirInlet.AirMassFlowRate +
    1173        98199 :                         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        23892 :             this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1179        23892 :             this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
    1180        23892 :             HumRat = (this->dd_airterminalHotAirInlet.AirHumRat + this->dd_airterminalColdAirInlet.AirHumRat) / 2.0;
    1181        23892 :             Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy + this->dd_airterminalColdAirInlet.AirEnthalpy) / 2.0;
    1182              :         }
    1183       122091 :         Temperature = PsyTdbFnHW(Enthalpy, HumRat);
    1184              : 
    1185              :         // Load all properties in the damper outlet
    1186       122091 :         this->dd_airterminalOutlet.AirTemp = Temperature;
    1187       122091 :         this->dd_airterminalOutlet.AirHumRat = HumRat;
    1188       122091 :         this->dd_airterminalOutlet.AirMassFlowRate = MassFlow;
    1189       122091 :         this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlow;
    1190       122091 :         this->dd_airterminalOutlet.AirMassFlowRateMinAvail =
    1191       122091 :             min(this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail, this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail);
    1192       122091 :         this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
    1193              : 
    1194              :         // Calculate the hot and cold damper position in %
    1195       122091 :         if ((this->dd_airterminalHotAirInlet.AirMassFlowRateMax == 0.0) || (this->dd_airterminalColdAirInlet.AirMassFlowRateMax == 0.0)) {
    1196            8 :             this->ColdAirDamperPosition = 0.0;
    1197            8 :             this->HotAirDamperPosition = 0.0;
    1198              :         } else {
    1199       122083 :             this->ColdAirDamperPosition = this->dd_airterminalColdAirInlet.AirMassFlowRate / this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
    1200       122083 :             this->HotAirDamperPosition = this->dd_airterminalHotAirInlet.AirMassFlowRate / this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
    1201              :         }
    1202       122091 :     }
    1203              : 
    1204        16275 :     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        16275 :         QTotLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
    1236              :         // Calculate all of the required Cp's
    1237        16275 :         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        16275 :         CpAirSysHot = CpAirZn;
    1241        16275 :         CpAirSysCold = CpAirZn;
    1242              : 
    1243              :         // calculate supply air flow rate based on user specified OA requirement
    1244        16275 :         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        16275 :         if (this->availSched->getCurrentVal() == 0.0) {
    1251              :             // System is Off set massflow to 0.0
    1252         5931 :             MassFlow = 0.0;
    1253              : 
    1254        10344 :         } 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         6994 :             if (std::abs((CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
    1258         6994 :                     CpAirZn >
    1259              :                 HVAC::SmallTempDiff) {
    1260         6994 :                 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         6994 :             if (MassFlow <= (this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac)) {
    1267         5095 :                 MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
    1268         5095 :                 MassFlow = max(MassFlow, this->dd_airterminalHotAirInlet.AirMassFlowRateMinAvail);
    1269         1899 :             } else if (MassFlow >= this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail) {
    1270           46 :                 MassFlow = this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail;
    1271              :             }
    1272              : 
    1273              :             // Apply the zone maximum outdoor air fraction for VAV boxes - a TRACE feature
    1274         6994 :             if (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor > 1.0) {
    1275            0 :                 MassFlow *= state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor;
    1276              :             }
    1277              : 
    1278         6994 :             MassFlow = max(MassFlow, MassFlowBasedOnOA);
    1279         6994 :             MassFlow = min(MassFlow, this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail);
    1280              : 
    1281         3350 :         } 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         3341 :             if (std::abs((CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
    1285         3341 :                     CpAirZn >
    1286              :                 HVAC::SmallTempDiff) {
    1287         3341 :                 MassFlow =
    1288         3341 :                     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         3341 :             if ((MassFlow <= (this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac)) && (MassFlow >= 0.0)) {
    1296          212 :                 MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMax * this->ZoneMinAirFrac;
    1297          212 :                 MassFlow = max(MassFlow, this->dd_airterminalColdAirInlet.AirMassFlowRateMinAvail);
    1298         3129 :             } else if (MassFlow < 0.0) {
    1299          353 :                 MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
    1300         2776 :             } else if (MassFlow >= this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
    1301          462 :                 MassFlow = this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail;
    1302              :             }
    1303              : 
    1304              :             // Apply the zone maximum outdoor air fraction for VAV boxes - a TRACE feature
    1305         3341 :             if (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor > 1.0) {
    1306            0 :                 MassFlow *= state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).SupplyAirAdjustFactor;
    1307              :             }
    1308              : 
    1309         3341 :             MassFlow = max(MassFlow, MassFlowBasedOnOA);
    1310         3341 :             MassFlow = min(MassFlow, this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail);
    1311              : 
    1312            9 :         } else if ((this->dd_airterminalHotAirInlet.AirMassFlowRateMaxAvail > 0.0) ||
    1313            9 :                    (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            9 :             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        16275 :         if (MassFlow > HVAC::SmallMassFlow) {
    1337              :             // Determine the enthalpy required from Zone enthalpy and the zone load.
    1338        10335 :             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        10335 :             if (std::abs(this->dd_airterminalColdAirInlet.AirTemp - this->dd_airterminalHotAirInlet.AirTemp) > HVAC::SmallTempDiff) {
    1342              :                 // Calculate the Cold air mass flow rate
    1343        10158 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate =
    1344        10158 :                     (QZnReq - MassFlow * CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp) /
    1345        10158 :                     (CpAirSysCold * this->dd_airterminalColdAirInlet.AirTemp - CpAirSysHot * this->dd_airterminalHotAirInlet.AirTemp);
    1346          177 :             } else if ((QTotLoad > 0.0) && (this->dd_airterminalHotAirInlet.AirMassFlowRate > 0.0)) {
    1347            0 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1348              :             } else {
    1349          177 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
    1350              :             }
    1351              : 
    1352              :             // Need to make sure that the flows are within limits
    1353        10335 :             if (this->dd_airterminalColdAirInlet.AirMassFlowRate > this->dd_airterminalColdAirInlet.AirMassFlowRateMaxAvail) {
    1354          640 :                 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         9695 :             } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < 0.0) {
    1358          356 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1359         9339 :             } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate > MassFlow) {
    1360          673 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
    1361              :             }
    1362              :             // Using Mass Continuity to determine the other duct flow quantity
    1363        10335 :             this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow - this->dd_airterminalColdAirInlet.AirMassFlowRate;
    1364              : 
    1365        10335 :             if (this->dd_airterminalHotAirInlet.AirMassFlowRate < DualDuctMassFlowSetToler) {
    1366         3140 :                 this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
    1367         3140 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = MassFlow;
    1368         7195 :             } else if (this->dd_airterminalColdAirInlet.AirMassFlowRate < DualDuctMassFlowSetToler) {
    1369         1899 :                 this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1370         1899 :                 this->dd_airterminalHotAirInlet.AirMassFlowRate = MassFlow;
    1371              :             }
    1372              : 
    1373              :             // After the flow rates are determined the properties are calculated.
    1374        10335 :             HumRat = (this->dd_airterminalHotAirInlet.AirHumRat * this->dd_airterminalHotAirInlet.AirMassFlowRate +
    1375        10335 :                       this->dd_airterminalColdAirInlet.AirHumRat * this->dd_airterminalColdAirInlet.AirMassFlowRate) /
    1376              :                      MassFlow;
    1377        10335 :             Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy * this->dd_airterminalHotAirInlet.AirMassFlowRate +
    1378        10335 :                         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         5940 :             this->dd_airterminalColdAirInlet.AirMassFlowRate = 0.0;
    1384         5940 :             this->dd_airterminalHotAirInlet.AirMassFlowRate = 0.0;
    1385         5940 :             HumRat = (this->dd_airterminalHotAirInlet.AirHumRat + this->dd_airterminalColdAirInlet.AirHumRat) / 2.0;
    1386         5940 :             Enthalpy = (this->dd_airterminalHotAirInlet.AirEnthalpy + this->dd_airterminalColdAirInlet.AirEnthalpy) / 2.0;
    1387              :         }
    1388        16275 :         Temperature = Psychrometrics::PsyTdbFnHW(Enthalpy, HumRat);
    1389              : 
    1390        16275 :         this->dd_airterminalOutlet.AirTemp = Temperature;
    1391        16275 :         this->dd_airterminalOutlet.AirHumRat = HumRat;
    1392        16275 :         this->dd_airterminalOutlet.AirMassFlowRate = MassFlow;
    1393        16275 :         this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlow;
    1394        16275 :         this->dd_airterminalOutlet.AirMassFlowRateMinAvail = this->ZoneMinAirFrac * this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
    1395        16275 :         this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
    1396              : 
    1397              :         // Calculate the hot and cold damper position in %
    1398        16275 :         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        16275 :             this->ColdAirDamperPosition = this->dd_airterminalColdAirInlet.AirMassFlowRate / this->dd_airterminalColdAirInlet.AirMassFlowRateMax;
    1403        16275 :             this->HotAirDamperPosition = this->dd_airterminalHotAirInlet.AirMassFlowRate / this->dd_airterminalHotAirInlet.AirMassFlowRateMax;
    1404              :         }
    1405        16275 :     }
    1406              : 
    1407        67628 :     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        67628 :         OAInletNodeNum = this->OAInletNodeNum;
    1452        67628 :         if (this->RecircIsUsed) {
    1453        36888 :             RecircInletNodeNum = this->RecircAirInletNodeNum;
    1454              :         }
    1455              :         // Calculate required ventilation air flow rate based on user specified OA requirement
    1456        67628 :         this->CalcOAOnlyMassFlow(state, OAMassFlow);
    1457              : 
    1458              :         // The calculated load from the Heat Balance, adjusted for any equipment sequenced before terminal
    1459        67628 :         QTotLoadRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired;
    1460        67628 :         QtoHeatSPRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP;
    1461        67628 :         QtoCoolSPRemain = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToCoolSP;
    1462              : 
    1463              :         // Calculate all of the required Cp's
    1464        67628 :         CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    1465        67628 :         CpAirSysOA = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(OAInletNodeNum).HumRat);
    1466        67628 :         if (this->RecircIsUsed) {
    1467        36888 :             CpAirSysRA = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(RecircInletNodeNum).HumRat);
    1468              :         }
    1469              : 
    1470              :         // Set the OA Damper to the calculated ventilation flow rate
    1471        67628 :         this->dd_airterminalOAInlet.AirMassFlowRate = OAMassFlow;
    1472              :         // Need to make sure that the OA flows are within limits
    1473        67628 :         if (this->dd_airterminalOAInlet.AirMassFlowRate > this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail) {
    1474           66 :             this->dd_airterminalOAInlet.AirMassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail;
    1475        67562 :         } 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        67628 :         if (std::abs((CpAirSysOA * this->dd_airterminalOAInlet.AirTemp) - (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) / CpAirZn >
    1481              :             HVAC::SmallTempDiff) {
    1482       135086 :             QOALoad = this->dd_airterminalOAInlet.AirMassFlowRate *
    1483        67543 :                       (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp - CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    1484              : 
    1485        67543 :             auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ZoneNum);
    1486        67543 :             QOALoadToHeatSP =
    1487        67543 :                 this->dd_airterminalOAInlet.AirMassFlowRate * (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp - CpAirZn * zoneTstatSetpt.setptLo);
    1488        67543 :             QOALoadToCoolSP =
    1489        67543 :                 this->dd_airterminalOAInlet.AirMassFlowRate * (CpAirSysOA * this->dd_airterminalOAInlet.AirTemp - CpAirZn * zoneTstatSetpt.setptHi);
    1490              : 
    1491              :         } else {
    1492           85 :             QOALoad = 0.0;
    1493           85 :             QOALoadToHeatSP = 0.0;
    1494           85 :             QOALoadToCoolSP = 0.0;
    1495              :         }
    1496              : 
    1497        67628 :         if (this->RecircIsUsed) {
    1498              : 
    1499              :             // correct load for recirc side to account for impact of OA side
    1500              :             // QTotRemainAdjust      = QTotLoadRemain  - QOALoad
    1501        36888 :             QtoHeatSPRemainAdjust = QtoHeatSPRemain - QOALoadToHeatSP;
    1502        36888 :             QtoCoolSPRemainAdjust = QtoCoolSPRemain - QOALoadToCoolSP;
    1503              : 
    1504        36888 :             if (QtoCoolSPRemainAdjust < 0.0) {
    1505        23594 :                 QRALoad = QtoCoolSPRemainAdjust;
    1506        13294 :             } else if (QtoHeatSPRemainAdjust > 0.0) {
    1507        12180 :                 QRALoad = QtoHeatSPRemainAdjust;
    1508              :             } else {
    1509         1114 :                 QRALoad = 0.0;
    1510              :             }
    1511              : 
    1512        36888 :             if (QRALoad < 0.0) {                                                                                         // cooling
    1513        23594 :                 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        70782 :                     if (std::abs((CpAirSysRA * this->dd_airterminalRecircAirInlet.AirTemp) -
    1516        23594 :                                  (CpAirZn * state.dataLoopNodes->Node(ZoneNodeNum).Temp)) /
    1517        23594 :                             CpAirZn >
    1518              :                         HVAC::SmallTempDiff) {
    1519        47188 :                         this->dd_airterminalRecircAirInlet.AirMassFlowRate = QRALoad / (CpAirSysRA * this->dd_airterminalRecircAirInlet.AirTemp -
    1520        23594 :                                                                                         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        13294 :                 this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
    1528              :             }
    1529              : 
    1530              :             // Need to make sure that the RA flows are within limits
    1531        36888 :             if (this->dd_airterminalRecircAirInlet.AirMassFlowRate > this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail) {
    1532           25 :                 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        36863 :             } else if (this->dd_airterminalRecircAirInlet.AirMassFlowRate < 0.0) {
    1535            0 :                 this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
    1536              :             }
    1537              : 
    1538              :         } else {
    1539        30740 :             this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
    1540        30740 :             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        67628 :         if (((std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2) <
    1546       121947 :               this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag) ||
    1547        54319 :              (std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3) <
    1548       135256 :               this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag)) &&
    1549        13309 :             (std::abs(this->dd_airterminalRecircAirInlet.AirMassFlowRate - this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1) >=
    1550        13309 :              this->dd_airterminalRecircAirInlet.AirMassFlowDiffMag)) {
    1551           16 :             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        67628 :         MassFlowMax = this->dd_airterminalOAInlet.AirMassFlowRateMaxAvail + this->dd_airterminalRecircAirInlet.AirMassFlowRateMaxAvail;
    1558        67628 :         if (this->availSched->getCurrentVal() > 0.0) {
    1559        67628 :             TotMassFlow = this->dd_airterminalOAInlet.AirMassFlowRate + this->dd_airterminalRecircAirInlet.AirMassFlowRate;
    1560              :         } else {
    1561            0 :             TotMassFlow = 0.0;
    1562              :         }
    1563              : 
    1564        67628 :         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        67408 :             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        67408 :             TotMassFlow = this->dd_airterminalOAInlet.AirMassFlowRate + this->dd_airterminalRecircAirInlet.AirMassFlowRate;
    1572        67408 :             if (TotMassFlow > HVAC::SmallMassFlow) {
    1573        67408 :                 HumRat = (this->dd_airterminalOAInlet.AirHumRat * this->dd_airterminalOAInlet.AirMassFlowRate +
    1574        67408 :                           this->dd_airterminalRecircAirInlet.AirHumRat * this->dd_airterminalRecircAirInlet.AirMassFlowRate) /
    1575              :                          TotMassFlow;
    1576        67408 :                 Enthalpy = (this->dd_airterminalOAInlet.AirEnthalpy * this->dd_airterminalOAInlet.AirMassFlowRate +
    1577        67408 :                             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          220 :             this->dd_airterminalRecircAirInlet.AirMassFlowRate = 0.0;
    1587          220 :             this->dd_airterminalOAInlet.AirMassFlowRate = 0.0;
    1588          220 :             HumRat = (this->dd_airterminalRecircAirInlet.AirHumRat + this->dd_airterminalOAInlet.AirHumRat) / 2.0;
    1589          220 :             Enthalpy = (this->dd_airterminalRecircAirInlet.AirEnthalpy + this->dd_airterminalOAInlet.AirEnthalpy) / 2.0;
    1590              :         }
    1591              : 
    1592        67628 :         Temperature = Psychrometrics::PsyTdbFnHW(Enthalpy, HumRat);
    1593              : 
    1594        67628 :         this->dd_airterminalOutlet.AirTemp = Temperature;
    1595        67628 :         this->dd_airterminalOutlet.AirHumRat = HumRat;
    1596        67628 :         this->dd_airterminalOutlet.AirMassFlowRate = TotMassFlow;
    1597        67628 :         this->dd_airterminalOutlet.AirMassFlowRateMaxAvail = MassFlowMax;
    1598        67628 :         this->dd_airterminalOutlet.AirEnthalpy = Enthalpy;
    1599              : 
    1600              :         // Calculate the OA and RA damper position in %
    1601        67628 :         if (this->RecircIsUsed) {
    1602        36888 :             if (this->dd_airterminalRecircAirInlet.AirMassFlowRateMax == 0.0) { // protect div by zero
    1603            6 :                 this->RecircAirDamperPosition = 0.0;
    1604              :             } else {
    1605        36882 :                 this->RecircAirDamperPosition =
    1606        36882 :                     this->dd_airterminalRecircAirInlet.AirMassFlowRate / this->dd_airterminalRecircAirInlet.AirMassFlowRateMax;
    1607              :             }
    1608              :         }
    1609              : 
    1610        67628 :         if (this->dd_airterminalOAInlet.AirMassFlowRateMax == 0.0) { // protect div by zero
    1611           11 :             this->OADamperPosition = 0.0;
    1612              :         } else {
    1613        67617 :             this->OADamperPosition = this->dd_airterminalOAInlet.AirMassFlowRate / this->dd_airterminalOAInlet.AirMassFlowRateMax;
    1614              :         }
    1615              : 
    1616              :         // Calculate OAFraction of mixed air after the box
    1617        67628 :         if (TotMassFlow > 0) {
    1618        67408 :             if (this->RecircIsUsed) {
    1619        36768 :                 if (this->dd_airterminalOAInlet.AirMassFlowRate == 0.0) {
    1620            0 :                     this->OAFraction = 0.0;
    1621        36768 :                 } else if (this->dd_airterminalRecircAirInlet.AirMassFlowRate == 0.0) {
    1622        13193 :                     this->OAFraction = 1.0;
    1623              :                 } else {
    1624        23575 :                     this->OAFraction = this->dd_airterminalOAInlet.AirMassFlowRate / TotMassFlow;
    1625              :                 }
    1626              :             } else {
    1627        30640 :                 this->OAFraction = 1.0;
    1628              :             }
    1629              :         } else {
    1630          220 :             this->OAFraction = 0.0;
    1631              :         }
    1632              : 
    1633        67628 :         this->dd_airterminalRecircAirInlet.AirMassFlowRateHist3 = this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2;
    1634        67628 :         this->dd_airterminalRecircAirInlet.AirMassFlowRateHist2 = this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1;
    1635        67628 :         this->dd_airterminalRecircAirInlet.AirMassFlowRateHist1 = this->dd_airterminalRecircAirInlet.AirMassFlowRate;
    1636        67628 :     }
    1637              : 
    1638        16275 :     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        16275 :         SAMassFlow = 0.0;
    1659        16275 :         AirLoopOAFrac = 0.0;
    1660              : 
    1661              :         // Calculate the amount of OA based on optional user inputs
    1662        16275 :         if (AirLoopNum > 0) {
    1663        16272 :             AirLoopOAFrac = state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFrac;
    1664              :             // If no additional input from user, RETURN from subroutine
    1665        16272 :             if (this->NoOAFlowInputFromUser) {
    1666        16272 :                 return;
    1667              :             }
    1668              :             // Calculate outdoor air flow rate, zone multipliers are applied in GetInput
    1669            0 :             if (AirLoopOAFrac > 0.0) {
    1670            0 :                 bool constexpr UseMinOASchFlag(true); // Always use min OA schedule in calculations.
    1671              :                 Real64 const OAVolumeFlowRate =
    1672            0 :                     DataSizing::calcDesignSpecificationOutdoorAir(state,
    1673              :                                                                   this->OARequirementsPtr,
    1674              :                                                                   this->CtrlZoneNum,
    1675            0 :                                                                   state.dataAirLoop->AirLoopControlInfo(AirLoopNum).AirLoopDCVFlag,
    1676              :                                                                   UseMinOASchFlag);
    1677            0 :                 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            0 :                 SAMassFlow = OAMassFlow / AirLoopOAFrac;
    1681              :             }
    1682              :         }
    1683              :     }
    1684              : 
    1685        67639 :     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        67639 :         OAMassFlow = 0.0;
    1707              : 
    1708              :         // If no additional input from user, RETURN from subroutine
    1709        67639 :         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        67639 :         bool UseOccSchFlag = this->OAPerPersonMode == PerPersonMode::DCVByCurrentLevel; // TRUE = use actual occupancy, FALSE = use total zone people
    1720        67639 :         bool PerPersonNotSet = this->OAPerPersonMode != PerPersonMode::DCVByCurrentLevel && this->OAPerPersonMode != PerPersonMode::ByDesignLevel;
    1721              : 
    1722        67639 :         bool constexpr UseMinOASchFlag(true); // Always use min OA schedule in calculations.
    1723        67639 :         Real64 OAVolumeFlowRate = DataSizing::calcDesignSpecificationOutdoorAir(state,
    1724              :                                                                                 this->OARequirementsPtr,
    1725              :                                                                                 this->CtrlZoneNum,
    1726              :                                                                                 UseOccSchFlag,
    1727              :                                                                                 UseMinOASchFlag,
    1728        67639 :                                                                                 PerPersonNotSet); // outside air volume flow rate (m3/s)
    1729              : 
    1730        67639 :         OAMassFlow = OAVolumeFlowRate * state.dataEnvrn->StdRhoAir;
    1731              : 
    1732        67639 :         if (present(MaxOAVolFlow)) {
    1733           11 :             OAVolumeFlowRate = DataSizing::calcDesignSpecificationOutdoorAir(
    1734              :                 state, this->OARequirementsPtr, this->CtrlZoneNum, UseOccSchFlag, UseMinOASchFlag, false, true);
    1735           11 :             MaxOAVolFlow = OAVolumeFlowRate;
    1736              :         }
    1737              :     }
    1738              : 
    1739       205994 :     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       205994 :         if (this->DamperType == DualDuctDamper::ConstantVolume || this->DamperType == DualDuctDamper::VariableVolume) {
    1752              : 
    1753       138366 :             int OutletNode = this->OutletNodeNum;
    1754       138366 :             int HotInletNode = this->HotAirInletNodeNum;
    1755       138366 :             int ColdInletNode = this->ColdAirInletNodeNum;
    1756              : 
    1757              :             // Set the outlet air nodes of the Damper
    1758       138366 :             state.dataLoopNodes->Node(HotInletNode).MassFlowRate = this->dd_airterminalHotAirInlet.AirMassFlowRate;
    1759       138366 :             state.dataLoopNodes->Node(ColdInletNode).MassFlowRate = this->dd_airterminalColdAirInlet.AirMassFlowRate;
    1760       138366 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = this->dd_airterminalOutlet.AirMassFlowRate;
    1761       138366 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = this->dd_airterminalOutlet.AirMassFlowRate;
    1762       138366 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = this->dd_airterminalOutlet.AirMassFlowRateMinAvail;
    1763       138366 :             state.dataLoopNodes->Node(OutletNode).Temp = this->dd_airterminalOutlet.AirTemp;
    1764       138366 :             state.dataLoopNodes->Node(OutletNode).HumRat = this->dd_airterminalOutlet.AirHumRat;
    1765       138366 :             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       138366 :             state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(HotInletNode).Quality;
    1769       138366 :             state.dataLoopNodes->Node(OutletNode).Press = state.dataLoopNodes->Node(HotInletNode).Press;
    1770              : 
    1771       138366 :             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       138366 :             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       138366 :             this->CalcOutdoorAirVolumeFlowRate(state);
    1795              : 
    1796       205994 :         } else if (this->DamperType == DualDuctDamper::OutdoorAir) {
    1797              : 
    1798        67628 :             int OutletNode = this->OutletNodeNum;
    1799        67628 :             int OAInletNode = this->OAInletNodeNum;
    1800              :             // Set the outlet air nodes of the Damper
    1801        67628 :             state.dataLoopNodes->Node(OAInletNode).MassFlowRate = this->dd_airterminalOAInlet.AirMassFlowRate;
    1802        67628 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = this->dd_airterminalOutlet.AirMassFlowRate;
    1803        67628 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = this->dd_airterminalOutlet.AirMassFlowRate;
    1804        67628 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = this->dd_airterminalOutlet.AirMassFlowRateMinAvail;
    1805        67628 :             state.dataLoopNodes->Node(OutletNode).Temp = this->dd_airterminalOutlet.AirTemp;
    1806        67628 :             state.dataLoopNodes->Node(OutletNode).HumRat = this->dd_airterminalOutlet.AirHumRat;
    1807        67628 :             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        67628 :             state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(OAInletNode).Quality;
    1811        67628 :             state.dataLoopNodes->Node(OutletNode).Press = state.dataLoopNodes->Node(OAInletNode).Press;
    1812              : 
    1813        67628 :             if (this->RecircIsUsed) {
    1814        36888 :                 int RAInletNode = this->RecircAirInletNodeNum;
    1815        36888 :                 state.dataLoopNodes->Node(RAInletNode).MassFlowRate = this->dd_airterminalRecircAirInlet.AirMassFlowRate;
    1816        36888 :                 if (state.dataLoopNodes->Node(OutletNode).MassFlowRate > 0.0) {
    1817        36768 :                     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        36768 :                     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          120 :                     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          120 :                     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        30740 :                 if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1842            0 :                     state.dataLoopNodes->Node(OutletNode).CO2 = state.dataLoopNodes->Node(OAInletNode).CO2;
    1843              :                 }
    1844        30740 :                 if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1845            0 :                     state.dataLoopNodes->Node(OutletNode).GenContam = state.dataLoopNodes->Node(OAInletNode).GenContam;
    1846              :                 }
    1847              :             }
    1848              :         }
    1849       205994 :     }
    1850              : 
    1851          800 :     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          800 :         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          800 :         if (!allocated(state.dataDualDuct->dd_airterminal)) {
    1873          792 :             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            8 :         print(state.files.bnd, "{}\n", "! ===============================================================");
    1879            8 :         print(state.files.bnd, "{}\n", Format_100);
    1880            8 :         print(state.files.bnd, " #Dual Duct Damper Connections,{}\n", state.dataDualDuct->NumDDAirTerminal * 2);
    1881            8 :         print(state.files.bnd, "{}\n", Format_102);
    1882              : 
    1883           42 :         for (int Count1 = 1; Count1 <= state.dataDualDuct->NumDDAirTerminal; ++Count1) {
    1884              : 
    1885              :             // Determine if this damper is connected to a supply air path
    1886           34 :             int Found = 0;
    1887           34 :             int SupplyAirPathNum = 0;
    1888           40 :             for (int Count2 = 1; Count2 <= state.dataZoneEquip->NumSupplyAirPaths; ++Count2) {
    1889           40 :                 SupplyAirPathNum = Count2;
    1890           40 :                 Found = 0;
    1891          210 :                 for (int Count3 = 1; Count3 <= state.dataZoneEquip->SupplyAirPath(Count2).NumOutletNodes; ++Count3) {
    1892          170 :                     if (state.dataDualDuct->dd_airterminal(Count1).HotAirInletNodeNum ==
    1893          170 :                         state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3)) {
    1894           23 :                         Found = Count3;
    1895              :                     }
    1896          170 :                     if (state.dataDualDuct->dd_airterminal(Count1).ColdAirInletNodeNum ==
    1897          170 :                         state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3)) {
    1898            0 :                         Found = Count3;
    1899              :                     }
    1900          170 :                     if (state.dataDualDuct->dd_airterminal(Count1).OAInletNodeNum == state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3)) {
    1901           11 :                         Found = Count3;
    1902              :                     }
    1903          170 :                     if (state.dataDualDuct->dd_airterminal(Count1).RecircAirInletNodeNum ==
    1904          170 :                         state.dataZoneEquip->SupplyAirPath(Count2).OutletNode(Count3)) {
    1905            0 :                         Found = Count3;
    1906              :                     }
    1907              :                 }
    1908           40 :                 if (Found != 0) {
    1909           34 :                     break;
    1910              :                 }
    1911              :             }
    1912           34 :             if (Found == 0) {
    1913            0 :                 SupplyAirPathNum = 0;
    1914              :             }
    1915              : 
    1916              :             // Determine which air loop this dual duct damper is connected to
    1917           34 :             Found = 0;
    1918           34 :             std::string ChrName;
    1919           40 :             for (int Count2 = 1; Count2 <= NumPrimaryAirSys; ++Count2) {
    1920           40 :                 ChrName = state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName;
    1921           40 :                 Found = 0;
    1922          109 :                 for (int Count3 = 1; Count3 <= state.dataAirLoop->AirToZoneNodeInfo(Count2).NumSupplyNodes; ++Count3) {
    1923           69 :                     if (SupplyAirPathNum != 0) {
    1924           69 :                         if (state.dataZoneEquip->SupplyAirPath(SupplyAirPathNum).InletNodeNum ==
    1925           69 :                             state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipSupplyNodeNum(Count3)) {
    1926           34 :                             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           40 :                 if (Found != 0) {
    1948           34 :                     break;
    1949              :                 }
    1950              :             }
    1951           34 :             if (Found == 0) {
    1952            0 :                 ChrName = "**Unknown**";
    1953              :             }
    1954              : 
    1955           34 :             std::string_view damperType = cmoNameArray[static_cast<int>(state.dataDualDuct->dd_airterminal(Count1).DamperType)];
    1956           48 :             if ((state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::ConstantVolume) ||
    1957           14 :                 (state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::VariableVolume)) {
    1958           23 :                 print(state.files.bnd,
    1959              :                       " Dual Duct Damper,{},{},{},{},{},Hot Air,{}\n",
    1960              :                       Count1,
    1961              :                       damperType,
    1962           23 :                       state.dataDualDuct->dd_airterminal(Count1).Name,
    1963           23 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).HotAirInletNodeNum),
    1964           23 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
    1965              :                       ChrName);
    1966              : 
    1967           23 :                 print(state.files.bnd,
    1968              :                       " Dual Duct Damper,{},{},{},{},{},Cold Air,{}\n",
    1969              :                       Count1,
    1970              :                       damperType,
    1971           23 :                       state.dataDualDuct->dd_airterminal(Count1).Name,
    1972           23 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).ColdAirInletNodeNum),
    1973           23 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
    1974              :                       ChrName);
    1975              : 
    1976           11 :             } else if (state.dataDualDuct->dd_airterminal(Count1).DamperType == DualDuctDamper::OutdoorAir) {
    1977           11 :                 print(state.files.bnd,
    1978              :                       "Dual Duct Damper, {},{},{},{},{},Outdoor Air,{}\n",
    1979              :                       Count1,
    1980              :                       damperType,
    1981           11 :                       state.dataDualDuct->dd_airterminal(Count1).Name,
    1982           11 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OAInletNodeNum),
    1983           11 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
    1984              :                       ChrName);
    1985           11 :                 print(state.files.bnd,
    1986              :                       "Dual Duct Damper, {},{},{},{},{},Recirculated Air,{}\n",
    1987              :                       Count1,
    1988              :                       damperType,
    1989           11 :                       state.dataDualDuct->dd_airterminal(Count1).Name,
    1990           11 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).RecircAirInletNodeNum),
    1991           11 :                       state.dataLoopNodes->NodeID(state.dataDualDuct->dd_airterminal(Count1).OutletNodeNum),
    1992              :                       ChrName);
    1993              :             }
    1994           34 :         }
    1995              :     }
    1996              : 
    1997           11 :     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           11 :         RecircIsUsed = true;
    2013           11 :         if (state.dataDualDuct->GetDualDuctOutdoorAirRecircUseFirstTimeOnly) {
    2014            1 :             state.dataDualDuct->NumDualDuctVarVolOA = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCMO_DDVarVolOA);
    2015            1 :             state.dataDualDuct->RecircIsUsedARR.allocate(state.dataDualDuct->NumDualDuctVarVolOA);
    2016            1 :             state.dataDualDuct->DamperNamesARR.allocate(state.dataDualDuct->NumDualDuctVarVolOA);
    2017            1 :             if (state.dataDualDuct->NumDualDuctVarVolOA > 0) {
    2018            1 :                 Array1D<Real64> NumArray(2, 0.0);
    2019            1 :                 Array1D_string AlphArray(7);
    2020            1 :                 Array1D_string cAlphaFields(7);       // Alpha field names
    2021            1 :                 Array1D_string cNumericFields(2);     // Numeric field names
    2022            1 :                 Array1D_bool lAlphaBlanks(7, true);   // Logical array, alpha field input BLANK = .TRUE.
    2023            1 :                 Array1D_bool lNumericBlanks(2, true); // Logical array, numeric field input BLANK = .TRUE.
    2024           12 :                 for (int DamperIndex = 1; DamperIndex <= state.dataDualDuct->NumDualDuctVarVolOA; ++DamperIndex) {
    2025              : 
    2026              :                     int NumAlphas;
    2027              :                     int NumNums;
    2028              :                     int IOStat;
    2029           11 :                     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           11 :                     state.dataDualDuct->DamperNamesARR(DamperIndex) = AlphArray(1);
    2042           11 :                     if (!lAlphaBlanks(5)) {
    2043            6 :                         state.dataDualDuct->RecircIsUsedARR(DamperIndex) = true;
    2044              :                     } else {
    2045            5 :                         state.dataDualDuct->RecircIsUsedARR(DamperIndex) = false;
    2046              :                     }
    2047              :                 }
    2048            1 :             }
    2049            1 :             state.dataDualDuct->GetDualDuctOutdoorAirRecircUseFirstTimeOnly = false;
    2050              :         }
    2051              : 
    2052           11 :         int DamperIndex = Util::FindItemInList(CompName, state.dataDualDuct->DamperNamesARR, state.dataDualDuct->NumDualDuctVarVolOA);
    2053           11 :         if (DamperIndex > 0) {
    2054           11 :             RecircIsUsed = state.dataDualDuct->RecircIsUsedARR(DamperIndex);
    2055              :         }
    2056           11 :     }
    2057              : 
    2058       138366 :     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       138366 :         if (this->AirLoopNum > 0) {
    2062       138343 :             this->OutdoorAirFlowRate =
    2063       138343 :                 (this->dd_airterminalOutlet.AirMassFlowRate / state.dataEnvrn->StdRhoAir) * state.dataAirLoop->AirLoopFlow(this->AirLoopNum).OAFrac;
    2064              :         }
    2065       138366 :     }
    2066              : 
    2067           34 :     void DualDuctAirTerminal::reportTerminalUnit(EnergyPlusData &state)
    2068              :     {
    2069              :         // populate the predefined equipment summary report related to air terminals
    2070           34 :         auto &orp = state.dataOutRptPredefined;
    2071           34 :         auto &adu = state.dataDefineEquipment->AirDistUnit(this->ADUNum);
    2072           34 :         if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
    2073           19 :             auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
    2074           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
    2075           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
    2076           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
    2077           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
    2078           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
    2079           19 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
    2080              :         }
    2081              : 
    2082           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, dualDuctDamperNames[(int)this->DamperType]);
    2083           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->MaxAirVolFlowRate);
    2084           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, "n/a");
    2085           34 :         if (this->zoneTurndownMinAirFracSched != nullptr) {
    2086            0 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, this->zoneTurndownMinAirFracSched->Name);
    2087              :         } else {
    2088           34 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
    2089              :         }
    2090           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
    2091           34 :         std::string schName = "n/a";
    2092           34 :         if (this->OARequirementsPtr > 0) {
    2093           11 :             schName = state.dataSize->OARequirements(this->OARequirementsPtr).oaFlowFracSched->Name;
    2094              :         }
    2095           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, schName);
    2096           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, "n/a");
    2097           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "n/a");
    2098           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, "n/a");
    2099           34 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, "n/a");
    2100           34 :     }
    2101              : 
    2102              : } // namespace DualDuct
    2103              : 
    2104              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1