LCOV - code coverage report
Current view: top level - EnergyPlus - DualDuct.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 818 993 82.4 %
Date: 2024-08-24 18:31:18 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14