LCOV - code coverage report
Current view: top level - EnergyPlus - DualDuct.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 26.2 % 985 258
Test Date: 2025-05-22 16:09:37 Functions: 57.1 % 14 8

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

Generated by: LCOV version 2.0-1