LCOV - code coverage report
Current view: top level - EnergyPlus - ZonePlenum.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 80.1 % 643 515
Test Date: 2025-05-22 16:09:37 Functions: 100.0 % 13 13

            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 <algorithm>
      50              : #include <cmath>
      51              : 
      52              : // EnergyPlus Headers
      53              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      54              : #include <EnergyPlus/DataContaminantBalance.hh>
      55              : #include <EnergyPlus/DataEnvironment.hh>
      56              : #include <EnergyPlus/DataHeatBalance.hh>
      57              : #include <EnergyPlus/DataLoopNode.hh>
      58              : #include <EnergyPlus/DataZoneEquipment.hh>
      59              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      60              : #include <EnergyPlus/NodeInputManager.hh>
      61              : #include <EnergyPlus/PoweredInductionUnits.hh>
      62              : #include <EnergyPlus/Psychrometrics.hh>
      63              : #include <EnergyPlus/PurchasedAirManager.hh>
      64              : #include <EnergyPlus/UtilityRoutines.hh>
      65              : #include <EnergyPlus/ZonePlenum.hh>
      66              : 
      67              : namespace EnergyPlus::ZonePlenum {
      68              : // Module containing simulation routines for both zone return and zone supply plenums
      69              : 
      70              : // MODULE INFORMATION:
      71              : //       AUTHOR         Peter Graham Ellis
      72              : //       DATE WRITTEN   November 2000
      73              : //       MODIFIED       na
      74              : //       RE-ENGINEERED  na
      75              : 
      76              : // PURPOSE OF THIS MODULE:
      77              : // To encapsulate the data and algorithms required to
      78              : // manage Air Path Zone Return Plenum Components
      79              : 
      80              : // METHODOLOGY EMPLOYED:
      81              : // The Zone Plenum
      82              : 
      83              : // Using/Aliasing
      84              : using namespace DataLoopNode;
      85              : using Psychrometrics::PsyHFnTdbW;
      86              : using Psychrometrics::PsyTdbFnHW;
      87              : 
      88              : // Functions
      89              : 
      90           10 : void SimAirZonePlenum(EnergyPlusData &state,
      91              :                       std::string_view CompName,
      92              :                       DataZoneEquipment::AirLoopHVACZone const iCompType,
      93              :                       int &CompIndex,
      94              :                       ObjexxFCL::Optional_bool_const FirstHVACIteration, // Autodesk:OPTIONAL Used without PRESENT check
      95              :                       ObjexxFCL::Optional_bool_const FirstCall,          // Autodesk:OPTIONAL Used without PRESENT check
      96              :                       ObjexxFCL::Optional_bool PlenumInletChanged        // Autodesk:OPTIONAL Used without PRESENT check
      97              : )
      98              : {
      99              : 
     100              :     // SUBROUTINE INFORMATION:
     101              :     //       AUTHOR         Peter Graham Ellis
     102              :     //       DATE WRITTEN   November 2000
     103              :     //       MODIFIED       March 2000
     104              :     //       RE-ENGINEERED  na
     105              : 
     106              :     // PURPOSE OF THIS SUBROUTINE:
     107              :     // This subroutine manages the ZonePlenum component simulation for both
     108              :     // return and supply plenums.
     109              :     // It is called from the SimAirLoopComponent at the system time step.
     110              : 
     111              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     112              :     int ZonePlenumNum; // The ZonePlenum that you are currently loading input into
     113              : 
     114              :     // Obtains and Allocates ZonePlenum related parameters from input file
     115           10 :     if (state.dataZonePlenum->GetInputFlag) { // First time subroutine has been entered
     116            4 :         GetZonePlenumInput(state);
     117            4 :         state.dataZonePlenum->GetInputFlag = false;
     118              :     }
     119              : 
     120           10 :     if (iCompType == DataZoneEquipment::AirLoopHVACZone::ReturnPlenum) { // 'AirLoopHVAC:ReturnPlenum'
     121              :         // Find the correct ZonePlenumNumber
     122            8 :         if (CompIndex == 0) {
     123            5 :             ZonePlenumNum = Util::FindItemInList(CompName, state.dataZonePlenum->ZoneRetPlenCond, &ZoneReturnPlenumConditions::ZonePlenumName);
     124            5 :             if (ZonePlenumNum == 0) {
     125            0 :                 ShowFatalError(state, format("SimAirZonePlenum: AirLoopHVAC:ReturnPlenum not found={}", CompName));
     126              :             }
     127            5 :             CompIndex = ZonePlenumNum;
     128              :         } else {
     129            3 :             ZonePlenumNum = CompIndex;
     130            3 :             if (ZonePlenumNum > state.dataZonePlenum->NumZoneReturnPlenums || ZonePlenumNum < 1) {
     131            0 :                 ShowFatalError(
     132              :                     state,
     133            0 :                     format("SimAirZonePlenum: Invalid CompIndex passed={}, Number of AirLoopHVAC:ReturnPlenum={}, AirLoopHVAC:ReturnPlenum name={}",
     134              :                            ZonePlenumNum,
     135            0 :                            state.dataZonePlenum->NumZoneReturnPlenums,
     136              :                            CompName));
     137              :             }
     138            3 :             if (state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).checkEquipName) {
     139            2 :                 if (CompName != state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZonePlenumName) {
     140            0 :                     ShowFatalError(state,
     141            0 :                                    format("SimAirZonePlenum: Invalid CompIndex passed={}, AirLoopHVAC:ReturnPlenum name={}, stored "
     142              :                                           "AirLoopHVAC:ReturnPlenum Name for that index={}",
     143              :                                           ZonePlenumNum,
     144              :                                           CompName,
     145            0 :                                           state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZonePlenumName));
     146              :                 }
     147            2 :                 state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).checkEquipName = false;
     148              :             }
     149              :         }
     150              : 
     151            8 :         InitAirZoneReturnPlenum(state, ZonePlenumNum); // Initialize all ZonePlenum related parameters
     152              : 
     153            8 :         CalcAirZoneReturnPlenum(state, ZonePlenumNum);
     154              : 
     155            8 :         UpdateAirZoneReturnPlenum(state, ZonePlenumNum); // Update the current ZonePlenum to the outlet nodes
     156              : 
     157            2 :     } else if (iCompType == DataZoneEquipment::AirLoopHVACZone::SupplyPlenum) { // 'AirLoopHVAC:SupplyPlenum'
     158              :         // Find the correct ZonePlenumNumber
     159            2 :         if (CompIndex == 0) {
     160            1 :             ZonePlenumNum = Util::FindItemInList(CompName, state.dataZonePlenum->ZoneSupPlenCond, &ZoneSupplyPlenumConditions::ZonePlenumName);
     161            1 :             if (ZonePlenumNum == 0) {
     162            0 :                 ShowFatalError(state, format("SimAirZonePlenum: AirLoopHVAC:SupplyPlenum not found={}", CompName));
     163              :             }
     164            1 :             CompIndex = ZonePlenumNum;
     165              :         } else {
     166            1 :             ZonePlenumNum = CompIndex;
     167            1 :             if (ZonePlenumNum > state.dataZonePlenum->NumZoneSupplyPlenums || ZonePlenumNum < 1) {
     168            0 :                 ShowFatalError(
     169              :                     state,
     170            0 :                     format("SimAirZonePlenum: Invalid CompIndex passed={}, Number of AirLoopHVAC:SupplyPlenum={}, AirLoopHVAC:SupplyPlenum name={}",
     171              :                            ZonePlenumNum,
     172            0 :                            state.dataZonePlenum->NumZoneReturnPlenums,
     173              :                            CompName));
     174              :             }
     175            1 :             if (state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).checkEquipName) {
     176            1 :                 if (CompName != state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZonePlenumName) {
     177            0 :                     ShowFatalError(state,
     178            0 :                                    format("SimAirZonePlenum: Invalid CompIndex passed={}, AirLoopHVAC:SupplyPlenum name={}, stored "
     179              :                                           "AirLoopHVAC:SupplyPlenum Name for that index={}",
     180              :                                           ZonePlenumNum,
     181              :                                           CompName,
     182            0 :                                           state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZonePlenumName));
     183              :                 }
     184            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).checkEquipName = false;
     185              :             }
     186              :         }
     187              : 
     188            2 :         InitAirZoneSupplyPlenum(state, ZonePlenumNum, FirstHVACIteration, FirstCall); // Initialize all ZonePlenum related parameters
     189              : 
     190            2 :         CalcAirZoneSupplyPlenum(state, ZonePlenumNum, FirstCall);
     191              :         // Update the current ZonePlenum to the outlet nodes
     192            2 :         UpdateAirZoneSupplyPlenum(state, ZonePlenumNum, PlenumInletChanged, FirstCall);
     193              : 
     194              :     } else {
     195            0 :         ShowSevereError(state, format("SimAirZonePlenum: Errors in Plenum={}", CompName));
     196            0 :         ShowContinueError(state, format("ZonePlenum: Unhandled plenum type found:{}", iCompType));
     197            0 :         ShowFatalError(state, "Preceding conditions cause termination.");
     198              :     }
     199           10 : }
     200              : 
     201           10 : void GetZonePlenumInput(EnergyPlusData &state)
     202              : {
     203              : 
     204              :     // SUBROUTINE INFORMATION:
     205              :     //       AUTHOR         Peter Graham Ellis
     206              :     //       DATE WRITTEN   November 2000
     207              :     //       MODIFIED       August 2003, FCW: For each zone with a return air plenum put the ZoneRetPlenCond
     208              :     //                       number for the return air plenum in the ZoneEquipConfig array for the zone
     209              :     //                       for later access to the zone's return air plenum conditions.
     210              :     //       RE-ENGINEERED  na
     211              : 
     212              :     // PURPOSE OF THIS SUBROUTINE:
     213              :     // This subroutine is the main routine to call other input routines and Get routines
     214              : 
     215              :     // METHODOLOGY EMPLOYED:
     216              :     // Uses the status flags to trigger events.
     217              : 
     218              :     // Using/Aliasing
     219              :     using DataZoneEquipment::EquipConfiguration;
     220              :     using NodeInputManager::CheckUniqueNodeNumbers;
     221              :     using NodeInputManager::EndUniqueNodeCheck;
     222              :     using NodeInputManager::GetNodeNums;
     223              :     using NodeInputManager::GetOnlySingleNode;
     224              :     using NodeInputManager::InitUniqueNodeCheck;
     225              :     using PoweredInductionUnits::PIUInducesPlenumAir;
     226              :     using PurchasedAirManager::CheckPurchasedAirForReturnPlenum;
     227              : 
     228              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     229              :     int ZoneEquipConfigLoop;
     230              :     int NumAlphas;
     231              :     int NumNums;
     232              :     int NumArgs;
     233              :     int NumNodes;
     234           10 :     Array1D_int NodeNums;
     235              :     int MaxNums;
     236              :     int MaxAlphas;
     237              :     int NodeNum;
     238              :     int IOStat;
     239           10 :     Array1D<Real64> NumArray;        // Numeric input items for object
     240           10 :     std::string CurrentModuleObject; // for ease in getting objects
     241           10 :     Array1D_string AlphArray;        // Alpha input items for object
     242           10 :     Array1D_string cAlphaFields;     // Alpha field names
     243           10 :     Array1D_string cNumericFields;   // Numeric field names
     244           10 :     Array1D_bool lAlphaBlanks;       // Logical array, alpha field input BLANK = .TRUE.
     245           10 :     Array1D_bool lNumericBlanks;     // Logical array, numeric field input BLANK = .TRUE.
     246           10 :     bool ErrorsFound(false);
     247              :     bool NodeListError; // Flag for node list error
     248              :     bool UniqueNodeError;
     249              :     static constexpr std::string_view RoutineName("GetZonePlenumInput: "); // include trailing blank space
     250           10 :     std::string InducedNodeListName;
     251              : 
     252           10 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "AirLoopHVAC:ReturnPlenum", NumArgs, NumAlphas, NumNums);
     253           10 :     MaxNums = NumNums;
     254           10 :     MaxAlphas = NumAlphas;
     255           10 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "AirLoopHVAC:SupplyPlenum", NumArgs, NumAlphas, NumNums);
     256           10 :     MaxNums = max(NumNums, MaxNums);
     257           10 :     MaxAlphas = max(NumAlphas, MaxAlphas);
     258           10 :     AlphArray.allocate(MaxAlphas);
     259           10 :     cAlphaFields.allocate(MaxAlphas);
     260           10 :     cNumericFields.allocate(MaxNums);
     261           10 :     NumArray.dimension(MaxNums, 0.0);
     262           10 :     lAlphaBlanks.dimension(MaxAlphas, true);
     263           10 :     lNumericBlanks.dimension(MaxNums, true);
     264           10 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "NodeList", NumArgs, NumAlphas, NumNums);
     265           10 :     NodeNums.dimension(NumArgs, 0);
     266              : 
     267           10 :     InducedNodeListName = "";
     268              : 
     269           10 :     state.dataZonePlenum->NumZoneReturnPlenums = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum");
     270           10 :     state.dataZonePlenum->NumZoneSupplyPlenums = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum");
     271              : 
     272           10 :     if (state.dataZonePlenum->NumZoneReturnPlenums > 0) {
     273            8 :         state.dataZonePlenum->ZoneRetPlenCond.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
     274              :     }
     275           10 :     if (state.dataZonePlenum->NumZoneSupplyPlenums > 0) {
     276            1 :         state.dataZonePlenum->ZoneSupPlenCond.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
     277              :     }
     278              : 
     279           10 :     InitUniqueNodeCheck(state, "AirLoopHVAC:ReturnPlenum");
     280           18 :     for (int ZonePlenumNum = 1; ZonePlenumNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++ZonePlenumNum) {
     281              : 
     282            8 :         CurrentModuleObject = "AirLoopHVAC:ReturnPlenum";
     283              : 
     284            8 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     285              :                                                                  CurrentModuleObject,
     286              :                                                                  ZonePlenumNum,
     287              :                                                                  AlphArray,
     288              :                                                                  NumAlphas,
     289              :                                                                  NumArray,
     290              :                                                                  NumNums,
     291              :                                                                  IOStat,
     292              :                                                                  lNumericBlanks,
     293              :                                                                  lAlphaBlanks,
     294              :                                                                  cAlphaFields,
     295              :                                                                  cNumericFields);
     296            8 :         Util::IsNameEmpty(state, AlphArray(1), CurrentModuleObject, ErrorsFound);
     297              : 
     298            8 :         auto &thisRetPlenum = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum);
     299            8 :         thisRetPlenum.ZonePlenumName = AlphArray(1);
     300              : 
     301              :         // Check if this zone is also used in another return plenum
     302            8 :         IOStat = Util::FindItemInList(AlphArray(2), state.dataZonePlenum->ZoneRetPlenCond, &ZoneReturnPlenumConditions::ZoneName, ZonePlenumNum - 1);
     303            8 :         if (IOStat != 0) {
     304            0 :             ShowSevereError(state,
     305            0 :                             format("{}{} \"{}\" is used more than once as a {}.", RoutineName, cAlphaFields(2), AlphArray(2), CurrentModuleObject));
     306            0 :             ShowContinueError(state, format("..Only one {} object may be connected to a given zone.", CurrentModuleObject));
     307            0 :             ShowContinueError(state, format("..occurs in {} = {}", CurrentModuleObject, AlphArray(1)));
     308            0 :             ErrorsFound = true;
     309              :         }
     310            8 :         thisRetPlenum.ZoneName = AlphArray(2);
     311              :         // put the X-Ref to the zone heat balance data structure
     312            8 :         thisRetPlenum.ActualZoneNum = Util::FindItemInList(AlphArray(2), state.dataHeatBal->Zone);
     313            8 :         if (thisRetPlenum.ActualZoneNum == 0) {
     314            0 :             ShowSevereError(state, format("For {} = {}, {} = {} not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
     315            0 :             ErrorsFound = true;
     316            0 :             continue;
     317              :         } else {
     318            8 :             state.dataHeatBal->Zone(thisRetPlenum.ActualZoneNum).IsReturnPlenum = true;
     319            8 :             state.dataHeatBal->Zone(thisRetPlenum.ActualZoneNum).PlenumCondNum = ZonePlenumNum;
     320              :         }
     321              :         //  Check if this zone is used as a controlled zone
     322            8 :         ZoneEquipConfigLoop = Util::FindItemInList(AlphArray(2), state.dataZoneEquip->ZoneEquipConfig, &EquipConfiguration::ZoneName);
     323            8 :         if (ZoneEquipConfigLoop != 0) {
     324            0 :             ShowSevereError(
     325              :                 state,
     326            0 :                 format(
     327              :                     "{}{} \"{}\" is a controlled zone. It cannot be used as a {}", RoutineName, cAlphaFields(2), AlphArray(2), CurrentModuleObject));
     328            0 :             ShowContinueError(state, format("..occurs in {} = {}", CurrentModuleObject, AlphArray(1)));
     329            0 :             ErrorsFound = true;
     330              :         }
     331              : 
     332            8 :         thisRetPlenum.ZoneNodeName = AlphArray(3);
     333            8 :         thisRetPlenum.ZoneNodeNum = GetOnlySingleNode(state,
     334            8 :                                                       AlphArray(3),
     335              :                                                       ErrorsFound,
     336              :                                                       DataLoopNode::ConnectionObjectType::AirLoopHVACReturnPlenum,
     337            8 :                                                       AlphArray(1),
     338              :                                                       DataLoopNode::NodeFluidType::Air,
     339              :                                                       DataLoopNode::ConnectionType::ZoneNode,
     340              :                                                       NodeInputManager::CompFluidStream::Primary,
     341              :                                                       ObjectIsNotParent);
     342              :         // Insert the Plenum Zone Number into the Zone Heat Balance data structure for later reference
     343            8 :         state.dataHeatBal->Zone(thisRetPlenum.ActualZoneNum).SystemZoneNodeNumber = thisRetPlenum.ZoneNodeNum;
     344              :         // SpaceHB TODO: For now, assign the same system node to the spaces in the zone
     345           16 :         for (int spaceNum : state.dataHeatBal->Zone(thisRetPlenum.ActualZoneNum).spaceIndexes) {
     346            8 :             state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber = thisRetPlenum.ZoneNodeNum;
     347              :         }
     348              : 
     349            8 :         thisRetPlenum.OutletNode = GetOnlySingleNode(state,
     350            8 :                                                      AlphArray(4),
     351              :                                                      ErrorsFound,
     352              :                                                      DataLoopNode::ConnectionObjectType::AirLoopHVACReturnPlenum,
     353            8 :                                                      AlphArray(1),
     354              :                                                      DataLoopNode::NodeFluidType::Air,
     355              :                                                      DataLoopNode::ConnectionType::Outlet,
     356              :                                                      NodeInputManager::CompFluidStream::Primary,
     357              :                                                      ObjectIsNotParent);
     358              : 
     359            8 :         InducedNodeListName = AlphArray(5);
     360            8 :         NodeListError = false;
     361           16 :         GetNodeNums(state,
     362              :                     InducedNodeListName,
     363              :                     NumNodes,
     364              :                     NodeNums,
     365              :                     NodeListError,
     366              :                     DataLoopNode::NodeFluidType::Air,
     367              :                     DataLoopNode::ConnectionObjectType::AirLoopHVACReturnPlenum,
     368            8 :                     thisRetPlenum.ZonePlenumName,
     369              :                     DataLoopNode::ConnectionType::InducedAir,
     370              :                     NodeInputManager::CompFluidStream::Primary,
     371              :                     ObjectIsNotParent,
     372              :                     false,
     373            8 :                     cAlphaFields(5));
     374              : 
     375            8 :         if (!NodeListError) {
     376            8 :             thisRetPlenum.NumInducedNodes = NumNodes;
     377            8 :             thisRetPlenum.InducedNode.allocate(thisRetPlenum.NumInducedNodes);
     378            8 :             thisRetPlenum.InducedMassFlowRate.allocate(thisRetPlenum.NumInducedNodes);
     379            8 :             thisRetPlenum.InducedMassFlowRateMaxAvail.allocate(thisRetPlenum.NumInducedNodes);
     380            8 :             thisRetPlenum.InducedMassFlowRateMinAvail.allocate(thisRetPlenum.NumInducedNodes);
     381            8 :             thisRetPlenum.InducedTemp.allocate(thisRetPlenum.NumInducedNodes);
     382            8 :             thisRetPlenum.InducedHumRat.allocate(thisRetPlenum.NumInducedNodes);
     383            8 :             thisRetPlenum.InducedEnthalpy.allocate(thisRetPlenum.NumInducedNodes);
     384            8 :             thisRetPlenum.InducedPressure.allocate(thisRetPlenum.NumInducedNodes);
     385            8 :             thisRetPlenum.InducedCO2.allocate(thisRetPlenum.NumInducedNodes);
     386            8 :             thisRetPlenum.InducedGenContam.allocate(thisRetPlenum.NumInducedNodes);
     387            8 :             thisRetPlenum.InducedMassFlowRate = 0.0;
     388            8 :             thisRetPlenum.InducedMassFlowRateMaxAvail = 0.0;
     389            8 :             thisRetPlenum.InducedMassFlowRateMinAvail = 0.0;
     390            8 :             thisRetPlenum.InducedTemp = 0.0;
     391            8 :             thisRetPlenum.InducedHumRat = 0.0;
     392            8 :             thisRetPlenum.InducedEnthalpy = 0.0;
     393            8 :             thisRetPlenum.InducedPressure = 0.0;
     394            8 :             thisRetPlenum.InducedCO2 = 0.0;
     395            8 :             thisRetPlenum.InducedGenContam = 0.0;
     396           14 :             for (NodeNum = 1; NodeNum <= NumNodes; ++NodeNum) {
     397            6 :                 thisRetPlenum.InducedNode(NodeNum) = NodeNums(NodeNum);
     398            6 :                 UniqueNodeError = false;
     399            6 :                 if (!CheckPurchasedAirForReturnPlenum(state, ZonePlenumNum)) {
     400           12 :                     CheckUniqueNodeNumbers(state, "Return Plenum Induced Air Nodes", UniqueNodeError, NodeNums(NodeNum), CurrentModuleObject);
     401            6 :                     if (UniqueNodeError) {
     402            0 :                         ShowContinueError(state, format("Occurs for ReturnPlenum = {}", AlphArray(1)));
     403            0 :                         ErrorsFound = true;
     404              :                     }
     405            6 :                     PIUInducesPlenumAir(state, thisRetPlenum.InducedNode(NodeNum), ZonePlenumNum);
     406              :                 }
     407              :             }
     408              :         } else {
     409            0 :             ShowContinueError(
     410              :                 state,
     411            0 :                 format("Invalid Induced Air Outlet Node or NodeList name in AirLoopHVAC:ReturnPlenum object = {}", thisRetPlenum.ZonePlenumName));
     412            0 :             ErrorsFound = true;
     413              :         }
     414              : 
     415            8 :         thisRetPlenum.NumInletNodes = NumAlphas - 5;
     416              : 
     417           16 :         for (auto &e : state.dataZonePlenum->ZoneRetPlenCond) {
     418            8 :             e.InitFlag = true;
     419              :         }
     420              : 
     421            8 :         thisRetPlenum.InletNode.allocate(thisRetPlenum.NumInletNodes);
     422            8 :         thisRetPlenum.InletMassFlowRate.allocate(thisRetPlenum.NumInletNodes);
     423            8 :         thisRetPlenum.InletMassFlowRateMaxAvail.allocate(thisRetPlenum.NumInletNodes);
     424            8 :         thisRetPlenum.InletMassFlowRateMinAvail.allocate(thisRetPlenum.NumInletNodes);
     425            8 :         thisRetPlenum.InletTemp.allocate(thisRetPlenum.NumInletNodes);
     426            8 :         thisRetPlenum.InletHumRat.allocate(thisRetPlenum.NumInletNodes);
     427            8 :         thisRetPlenum.InletEnthalpy.allocate(thisRetPlenum.NumInletNodes);
     428            8 :         thisRetPlenum.InletPressure.allocate(thisRetPlenum.NumInletNodes);
     429            8 :         thisRetPlenum.ZoneEqNum.allocate(thisRetPlenum.NumInletNodes);
     430              : 
     431            8 :         thisRetPlenum.InletNode = 0;
     432            8 :         thisRetPlenum.InletMassFlowRate = 0.0;
     433            8 :         thisRetPlenum.InletMassFlowRateMaxAvail = 0.0;
     434            8 :         thisRetPlenum.InletMassFlowRateMinAvail = 0.0;
     435            8 :         thisRetPlenum.InletTemp = 0.0;
     436            8 :         thisRetPlenum.InletHumRat = 0.0;
     437            8 :         thisRetPlenum.InletEnthalpy = 0.0;
     438            8 :         thisRetPlenum.InletPressure = 0.0;
     439            8 :         thisRetPlenum.OutletMassFlowRate = 0.0;
     440            8 :         thisRetPlenum.OutletMassFlowRateMaxAvail = 0.0;
     441            8 :         thisRetPlenum.OutletMassFlowRateMinAvail = 0.0;
     442            8 :         thisRetPlenum.OutletTemp = 0.0;
     443            8 :         thisRetPlenum.OutletHumRat = 0.0;
     444            8 :         thisRetPlenum.OutletEnthalpy = 0.0;
     445            8 :         thisRetPlenum.OutletPressure = 0.0;
     446            8 :         thisRetPlenum.ZoneTemp = 0.0;
     447            8 :         thisRetPlenum.ZoneHumRat = 0.0;
     448            8 :         thisRetPlenum.ZoneEnthalpy = 0.0;
     449              : 
     450           27 :         for (NodeNum = 1; NodeNum <= thisRetPlenum.NumInletNodes; ++NodeNum) {
     451              : 
     452           38 :             thisRetPlenum.InletNode(NodeNum) = GetOnlySingleNode(state,
     453           19 :                                                                  AlphArray(5 + NodeNum),
     454              :                                                                  ErrorsFound,
     455              :                                                                  DataLoopNode::ConnectionObjectType::AirLoopHVACReturnPlenum,
     456           19 :                                                                  AlphArray(1),
     457              :                                                                  DataLoopNode::NodeFluidType::Air,
     458              :                                                                  DataLoopNode::ConnectionType::Inlet,
     459              :                                                                  NodeInputManager::CompFluidStream::Primary,
     460              :                                                                  ObjectIsNotParent);
     461              :         }
     462              : 
     463              :     } // end AirLoopHVAC:ReturnPlenum Loop
     464           10 :     EndUniqueNodeCheck(state, "AirLoopHVAC:ReturnPlenum");
     465              : 
     466           11 :     for (int ZonePlenumNum = 1; ZonePlenumNum <= state.dataZonePlenum->NumZoneSupplyPlenums; ++ZonePlenumNum) {
     467            1 :         CurrentModuleObject = "AirLoopHVAC:SupplyPlenum";
     468              : 
     469            1 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     470              :                                                                  CurrentModuleObject,
     471              :                                                                  ZonePlenumNum,
     472              :                                                                  AlphArray,
     473              :                                                                  NumAlphas,
     474              :                                                                  NumArray,
     475              :                                                                  NumNums,
     476              :                                                                  IOStat,
     477              :                                                                  lNumericBlanks,
     478              :                                                                  lAlphaBlanks,
     479              :                                                                  cAlphaFields,
     480              :                                                                  cNumericFields);
     481            1 :         Util::IsNameEmpty(state, AlphArray(1), CurrentModuleObject, ErrorsFound);
     482              : 
     483            1 :         auto &thisSupPlenum = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum);
     484            1 :         thisSupPlenum.ZonePlenumName = AlphArray(1);
     485              : 
     486              :         // Check if this zone is also used in another plenum
     487            1 :         IOStat = Util::FindItemInList(AlphArray(2), state.dataZonePlenum->ZoneSupPlenCond, &ZoneSupplyPlenumConditions::ZoneName, ZonePlenumNum - 1);
     488            1 :         if (IOStat != 0) {
     489            0 :             ShowSevereError(state,
     490            0 :                             format("{}{} \"{}\" is used more than once as a {}.", RoutineName, cAlphaFields(2), AlphArray(2), CurrentModuleObject));
     491            0 :             ShowContinueError(state, format("..Only one {} object may be connected to a given zone.", CurrentModuleObject));
     492            0 :             ShowContinueError(state, format("..occurs in {} = {}", CurrentModuleObject, AlphArray(1)));
     493            0 :             ErrorsFound = true;
     494              :         }
     495            1 :         if (state.dataZonePlenum->NumZoneReturnPlenums > 0) { // Check if this zone is also used in another plenum
     496            1 :             IOStat = Util::FindItemInList(AlphArray(2), state.dataZonePlenum->ZoneRetPlenCond, &ZoneReturnPlenumConditions::ZoneName);
     497            1 :             if (IOStat != 0) {
     498            0 :                 ShowSevereError(state,
     499            0 :                                 format("{}{} \"{}\" is used more than once as a {} or AirLoopHVAC:ReturnPlenum.",
     500              :                                        RoutineName,
     501              :                                        cAlphaFields(2),
     502              :                                        AlphArray(2),
     503              :                                        CurrentModuleObject));
     504            0 :                 ShowContinueError(state,
     505            0 :                                   format("..Only one {} or AirLoopHVAC:ReturnPlenum object may be connected to a given zone.", CurrentModuleObject));
     506            0 :                 ShowContinueError(state, format("..occurs in {} = {}", CurrentModuleObject, AlphArray(1)));
     507            0 :                 ErrorsFound = true;
     508              :             }
     509              :         }
     510            1 :         thisSupPlenum.ZoneName = AlphArray(2);
     511              :         // put the X-Ref to the zone heat balance data structure
     512            1 :         thisSupPlenum.ActualZoneNum = Util::FindItemInList(AlphArray(2), state.dataHeatBal->Zone);
     513            1 :         if (thisSupPlenum.ActualZoneNum == 0) {
     514            0 :             ShowSevereError(state, format("For {} = {}, {} = {} not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
     515            0 :             ErrorsFound = true;
     516            0 :             continue;
     517              :         } else {
     518            1 :             state.dataHeatBal->Zone(thisSupPlenum.ActualZoneNum).IsSupplyPlenum = true;
     519            1 :             state.dataHeatBal->Zone(thisSupPlenum.ActualZoneNum).PlenumCondNum = ZonePlenumNum;
     520              :         }
     521              :         //  Check if this zone is used as a controlled zone
     522            1 :         if (std::any_of(state.dataZoneEquip->ZoneEquipConfig.begin(), state.dataZoneEquip->ZoneEquipConfig.end(), [](EquipConfiguration const &e) {
     523            3 :                 return e.IsControlled;
     524              :             })) {
     525            1 :             ZoneEquipConfigLoop = Util::FindItemInList(AlphArray(2), state.dataZoneEquip->ZoneEquipConfig, &EquipConfiguration::ZoneName);
     526            1 :             if (ZoneEquipConfigLoop != 0) {
     527            0 :                 ShowSevereError(state,
     528            0 :                                 format("{}{} \"{}\" is a controlled zone. It cannot be used as a {} or AirLoopHVAC:ReturnPlenum.",
     529              :                                        RoutineName,
     530              :                                        cAlphaFields(2),
     531              :                                        AlphArray(2),
     532              :                                        CurrentModuleObject));
     533            0 :                 ShowContinueError(state, format("..occurs in {} = {}", CurrentModuleObject, AlphArray(1)));
     534            0 :                 ErrorsFound = true;
     535              :             }
     536              :         }
     537              :         // Check if this is also used as a return plenum
     538              :         //  *** This next IF loop looks wrong.  Sent e-mail to Peter/Brent 8/14/08 for clarification ****
     539              :         //      IF (NumZoneReturnPlenums > 0) THEN
     540              :         //        IOSTAT=Util::FindItemInList(AlphArray(1),ZoneRetPlenCond%ZoneName,NumZoneReturnPlenums)
     541              :         //        IF (IOStat /= 0) THEN
     542              :         //          CALL ShowSevereError(state, RoutineName//'Plenum "'//TRIM(AlphArray(2))//  &
     543              :         //                               '" is a controlled zone.  It cannot be used as a '//  &
     544              :         //                               'SUPPLY PLENUM or RETURN PLENUM.')
     545              :         //          CALL ShowContinueError(state, '..occurs in '//TRIM(CurrentModuleObject)//' = '//TRIM(AlphArray(1)))
     546              :         //          ErrorsFound=.TRUE.
     547              :         //        ENDIF
     548              :         //      ENDIF
     549              : 
     550            1 :         thisSupPlenum.ZoneNodeName = AlphArray(3);
     551            1 :         thisSupPlenum.ZoneNodeNum = GetOnlySingleNode(state,
     552            1 :                                                       AlphArray(3),
     553              :                                                       ErrorsFound,
     554              :                                                       DataLoopNode::ConnectionObjectType::AirLoopHVACSupplyPlenum,
     555            1 :                                                       AlphArray(1),
     556              :                                                       DataLoopNode::NodeFluidType::Air,
     557              :                                                       DataLoopNode::ConnectionType::ZoneNode,
     558              :                                                       NodeInputManager::CompFluidStream::Primary,
     559              :                                                       ObjectIsNotParent);
     560              :         // Insert the Plenum Zone Number into the Zone Heat Balance data structure for later reference
     561            1 :         state.dataHeatBal->Zone(thisSupPlenum.ActualZoneNum).SystemZoneNodeNumber = thisSupPlenum.ZoneNodeNum;
     562              :         // SpaceHB TODO: For now, assign the same system node to the spaces in the zone
     563            2 :         for (int spaceNum : state.dataHeatBal->Zone(thisSupPlenum.ActualZoneNum).spaceIndexes) {
     564            1 :             state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber = thisSupPlenum.ZoneNodeNum;
     565              :         }
     566              : 
     567            1 :         thisSupPlenum.InletNode = GetOnlySingleNode(state,
     568            1 :                                                     AlphArray(4),
     569              :                                                     ErrorsFound,
     570              :                                                     DataLoopNode::ConnectionObjectType::AirLoopHVACSupplyPlenum,
     571            1 :                                                     AlphArray(1),
     572              :                                                     DataLoopNode::NodeFluidType::Air,
     573              :                                                     DataLoopNode::ConnectionType::Inlet,
     574              :                                                     NodeInputManager::CompFluidStream::Primary,
     575              :                                                     ObjectIsNotParent);
     576              : 
     577            1 :         thisSupPlenum.NumOutletNodes = NumAlphas - 4;
     578              : 
     579            2 :         for (auto &e : state.dataZonePlenum->ZoneSupPlenCond) {
     580            1 :             e.InitFlag = true;
     581              :         }
     582              : 
     583            1 :         thisSupPlenum.OutletNode.allocate(thisSupPlenum.NumOutletNodes);
     584            1 :         thisSupPlenum.OutletMassFlowRate.allocate(thisSupPlenum.NumOutletNodes);
     585            1 :         thisSupPlenum.OutletMassFlowRateMaxAvail.allocate(thisSupPlenum.NumOutletNodes);
     586            1 :         thisSupPlenum.OutletMassFlowRateMinAvail.allocate(thisSupPlenum.NumOutletNodes);
     587            1 :         thisSupPlenum.OutletTemp.allocate(thisSupPlenum.NumOutletNodes);
     588            1 :         thisSupPlenum.OutletHumRat.allocate(thisSupPlenum.NumOutletNodes);
     589            1 :         thisSupPlenum.OutletEnthalpy.allocate(thisSupPlenum.NumOutletNodes);
     590            1 :         thisSupPlenum.OutletPressure.allocate(thisSupPlenum.NumOutletNodes);
     591              : 
     592            1 :         thisSupPlenum.OutletNode = 0;
     593            1 :         thisSupPlenum.OutletMassFlowRate = 0.0;
     594            1 :         thisSupPlenum.OutletMassFlowRateMaxAvail = 0.0;
     595            1 :         thisSupPlenum.OutletMassFlowRateMinAvail = 0.0;
     596            1 :         thisSupPlenum.OutletTemp = 0.0;
     597            1 :         thisSupPlenum.OutletHumRat = 0.0;
     598            1 :         thisSupPlenum.OutletEnthalpy = 0.0;
     599            1 :         thisSupPlenum.OutletPressure = 0.0;
     600            1 :         thisSupPlenum.InletMassFlowRate = 0.0;
     601            1 :         thisSupPlenum.InletMassFlowRateMaxAvail = 0.0;
     602            1 :         thisSupPlenum.InletMassFlowRateMinAvail = 0.0;
     603            1 :         thisSupPlenum.InletTemp = 0.0;
     604            1 :         thisSupPlenum.InletHumRat = 0.0;
     605            1 :         thisSupPlenum.InletEnthalpy = 0.0;
     606            1 :         thisSupPlenum.InletPressure = 0.0;
     607            1 :         thisSupPlenum.ZoneTemp = 0.0;
     608            1 :         thisSupPlenum.ZoneHumRat = 0.0;
     609            1 :         thisSupPlenum.ZoneEnthalpy = 0.0;
     610              : 
     611            2 :         for (NodeNum = 1; NodeNum <= thisSupPlenum.NumOutletNodes; ++NodeNum) {
     612              : 
     613            2 :             thisSupPlenum.OutletNode(NodeNum) = GetOnlySingleNode(state,
     614            1 :                                                                   AlphArray(4 + NodeNum),
     615              :                                                                   ErrorsFound,
     616              :                                                                   DataLoopNode::ConnectionObjectType::AirLoopHVACSupplyPlenum,
     617            1 :                                                                   AlphArray(1),
     618              :                                                                   DataLoopNode::NodeFluidType::Air,
     619              :                                                                   DataLoopNode::ConnectionType::Outlet,
     620              :                                                                   NodeInputManager::CompFluidStream::Primary,
     621              :                                                                   ObjectIsNotParent);
     622              :         }
     623              : 
     624              :     } // end AirLoopHVAC:SupplyPlenum Loop
     625              : 
     626           10 :     AlphArray.deallocate();
     627           10 :     NumArray.deallocate();
     628           10 :     cAlphaFields.deallocate();
     629           10 :     cNumericFields.deallocate();
     630           10 :     lAlphaBlanks.deallocate();
     631           10 :     lNumericBlanks.deallocate();
     632           10 :     NodeNums.deallocate();
     633              : 
     634           10 :     if (ErrorsFound) {
     635            0 :         ShowFatalError(state, format("{}Errors found in input.  Preceding condition(s) cause termination.", RoutineName));
     636              :     }
     637           10 : }
     638              : 
     639            9 : void InitAirZoneReturnPlenum(EnergyPlusData &state, int const ZonePlenumNum)
     640              : {
     641              : 
     642              :     // SUBROUTINE INFORMATION:
     643              :     //       AUTHOR         Peter Graham Ellis
     644              :     //       DATE WRITTEN   November 2000
     645              :     //       MODIFIED       na
     646              :     //       RE-ENGINEERED  na
     647              : 
     648              :     // PURPOSE OF THIS SUBROUTINE:
     649              :     // This subroutine is for initializations of the ZonePlenum components.
     650              : 
     651              :     // METHODOLOGY EMPLOYED:
     652              :     // Uses the status flags to trigger events.
     653              : 
     654              :     // Using/Aliasing
     655              : 
     656              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     657              :     int InletNode;
     658              :     int ZoneNodeNum;
     659              :     int NodeNum;
     660              : 
     661              :     // Do the one time initializations
     662            9 :     if (state.dataZonePlenum->InitAirZoneReturnPlenumOneTimeFlag) {
     663              : 
     664              :         // For each zone with a return air plenum put the ZoneRetPlenCond number for the return air plenum
     665              :         // in the ZoneEquipConfig array for the zone. This allows direct access of the zone's return air
     666              :         // plenum conditions, such as plenum temperature and air flow. Also establish and save connections
     667              :         // to the Air Distribution Units. This is needed for the simple duct leakage calculation.
     668              : 
     669           14 :         for (int ZonePlenumLoop = 1; ZonePlenumLoop <= state.dataZonePlenum->NumZoneReturnPlenums; ++ZonePlenumLoop) {
     670            7 :             int NumADUsToPlen = 0;
     671            7 :             if (state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).NumInletNodes > 0) {
     672           21 :                 for (int InletNodeLoop = 1; InletNodeLoop <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).NumInletNodes; ++InletNodeLoop) {
     673           15 :                     InletNode = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).InletNode(InletNodeLoop);
     674              :                     // Loop through ZoneEquipConfig's and look for return air node value = InletNode
     675           86 :                     for (int ZoneEquipConfigLoop = 1; ZoneEquipConfigLoop <= state.dataGlobal->NumOfZones; ++ZoneEquipConfigLoop) {
     676           71 :                         if (!state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigLoop).IsControlled) continue;
     677          102 :                         for (int retNode = 1; retNode <= state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigLoop).NumReturnNodes; ++retNode) {
     678           51 :                             if (state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigLoop).ReturnNode(retNode) == InletNode) {
     679           14 :                                 state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigLoop).ReturnNodePlenumNum = ZonePlenumLoop;
     680           14 :                                 state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).ZoneEqNum(InletNodeLoop) = ZoneEquipConfigLoop;
     681              :                             }
     682              :                         }
     683              :                     }
     684              :                     // count the ADUs that can leak to this plenum
     685           65 :                     for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     686           50 :                         if (state.dataDefineEquipment->AirDistUnit(ADUNum).ZoneEqNum ==
     687           50 :                             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).ZoneEqNum(InletNodeLoop)) {
     688           14 :                             state.dataDefineEquipment->AirDistUnit(ADUNum).RetPlenumNum = ZonePlenumLoop;
     689           14 :                             ++NumADUsToPlen;
     690              :                         }
     691              :                     }
     692              :                 }
     693              :             }
     694            7 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).ADUIndex.allocate(NumADUsToPlen);
     695            7 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).NumADUs = NumADUsToPlen;
     696              :             // fill the list of air distribution units that can leak to this plenum
     697            7 :             if (NumADUsToPlen > 0) {
     698            5 :                 int ADUsToPlenIndex = 0;
     699           19 :                 for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     700           14 :                     if (state.dataDefineEquipment->AirDistUnit(ADUNum).RetPlenumNum == ZonePlenumLoop) {
     701           14 :                         ++ADUsToPlenIndex;
     702           14 :                         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumLoop).ADUIndex(ADUsToPlenIndex) = ADUNum;
     703              :                     }
     704              :                 }
     705              :             }
     706              :         }
     707              : 
     708              :         // Check that all ADUs with leakage found a return plenum
     709           21 :         for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     710           14 :             auto &thisADU(state.dataDefineEquipment->AirDistUnit(ADUNum));
     711              :             // TODO: the first half of this IF condition was a duplicated OR, if issues around this code, might want to check the history of this line
     712           14 :             if (thisADU.DownStreamLeak && (thisADU.RetPlenumNum == 0)) {
     713            0 :                 ShowWarningError(state,
     714            0 :                                  format("No return plenum found for simple duct leakage for ZoneHVAC:AirDistributionUnit={} in Zone={}",
     715            0 :                                         thisADU.Name,
     716            0 :                                         state.dataZoneEquip->ZoneEquipConfig(thisADU.ZoneEqNum).ZoneName));
     717            0 :                 ShowContinueError(state, "Leakage will be ignored for this ADU.");
     718            0 :                 thisADU.UpStreamLeak = false;
     719            0 :                 thisADU.DownStreamLeak = false;
     720            0 :                 thisADU.UpStreamLeakFrac = 0.0;
     721            0 :                 thisADU.DownStreamLeakFrac = 0.0;
     722              :             }
     723              :         }
     724              : 
     725            7 :         state.dataZonePlenum->InitAirZoneReturnPlenumOneTimeFlag = false;
     726              :     }
     727              : 
     728              :     // Do the Begin Environment initializations
     729            9 :     if (state.dataZonePlenum->InitAirZoneReturnPlenumEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
     730              : 
     731            2 :         for (int PlenumZoneNum = 1; PlenumZoneNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++PlenumZoneNum) {
     732              : 
     733            1 :             ZoneNodeNum = state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).ZoneNodeNum;
     734            1 :             state.dataLoopNodes->Node(ZoneNodeNum).Temp = 20.0;
     735            1 :             state.dataLoopNodes->Node(ZoneNodeNum).MassFlowRate = 0.0;
     736            1 :             state.dataLoopNodes->Node(ZoneNodeNum).Quality = 1.0;
     737            1 :             state.dataLoopNodes->Node(ZoneNodeNum).Press = state.dataEnvrn->OutBaroPress;
     738            1 :             state.dataLoopNodes->Node(ZoneNodeNum).HumRat = state.dataEnvrn->OutHumRat;
     739            1 :             state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy =
     740            1 :                 PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNodeNum).Temp, state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
     741              : 
     742            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).ZoneTemp = 20.0;
     743            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).ZoneHumRat = 0.0;
     744            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).ZoneEnthalpy = 0.0;
     745            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletTemp = 0.0;
     746            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletHumRat = 0.0;
     747            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletEnthalpy = 0.0;
     748            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletPressure = 0.0;
     749            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletMassFlowRate = 0.0;
     750            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletMassFlowRateMaxAvail = 0.0;
     751            1 :             state.dataZonePlenum->ZoneRetPlenCond(PlenumZoneNum).InletMassFlowRateMinAvail = 0.0;
     752              :         }
     753              : 
     754            1 :         state.dataZonePlenum->InitAirZoneReturnPlenumEnvrnFlag = false;
     755              :     }
     756              : 
     757            9 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     758            7 :         state.dataZonePlenum->InitAirZoneReturnPlenumEnvrnFlag = true;
     759              :     }
     760              : 
     761              :     // Transfer the node data to ZoneRetPlenCond data structure
     762           34 :     for (NodeNum = 1; NodeNum <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumInletNodes; ++NodeNum) {
     763              : 
     764           25 :         InletNode = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletNode(NodeNum);
     765              :         // Set all of the inlet mass flow variables from the nodes
     766           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRate(NodeNum) = state.dataLoopNodes->Node(InletNode).MassFlowRate;
     767           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRateMaxAvail(NodeNum) =
     768           25 :             state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail;
     769           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRateMinAvail(NodeNum) =
     770           25 :             state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail;
     771              :         //    ! Set all of the inlet state variables from the inlet nodes
     772              :         //    ZoneRetPlenCond(ZonePlenumNum)%InletTemp(NodeNum)         = Node(InletNode)%Temp
     773              :         //    ZoneRetPlenCond(ZonePlenumNum)%InletHumRat(NodeNum)       = Node(InletNode)%HumRat
     774              :         //    ZoneRetPlenCond(ZonePlenumNum)%InletEnthalpy(NodeNum)     = Node(InletNode)%Enthalpy
     775           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletPressure(NodeNum) = state.dataLoopNodes->Node(InletNode).Press;
     776              :     }
     777              : 
     778            9 :     ZoneNodeNum = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneNodeNum;
     779              :     // Set the induced air flow rates and conditions
     780           21 :     for (NodeNum = 1; NodeNum <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumInducedNodes; ++NodeNum) {
     781           12 :         int InducedNode = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedNode(NodeNum);
     782           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedMassFlowRate(NodeNum) = state.dataLoopNodes->Node(InducedNode).MassFlowRate;
     783           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedMassFlowRateMaxAvail(NodeNum) =
     784           12 :             state.dataLoopNodes->Node(InducedNode).MassFlowRateMaxAvail;
     785           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedMassFlowRateMinAvail(NodeNum) =
     786           12 :             state.dataLoopNodes->Node(InducedNode).MassFlowRateMinAvail;
     787              : 
     788           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedTemp(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).Temp;
     789           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedHumRat(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
     790           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedEnthalpy(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy;
     791           12 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedPressure(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).Press;
     792           12 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     793            2 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedCO2(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).CO2;
     794              :         }
     795           12 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     796            2 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedGenContam(NodeNum) = state.dataLoopNodes->Node(ZoneNodeNum).GenContam;
     797              :         }
     798              :     }
     799              : 
     800              :     // Add stuff to calculate conduction inputs to the zone plenum
     801              :     // Now load the zone conditions
     802            9 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneTemp = state.dataLoopNodes->Node(ZoneNodeNum).Temp;
     803            9 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneHumRat = state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
     804            9 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneEnthalpy = state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy;
     805            9 : }
     806              : 
     807            2 : void InitAirZoneSupplyPlenum(EnergyPlusData &state, int const ZonePlenumNum, bool const FirstHVACIteration, bool const FirstCall)
     808              : {
     809              : 
     810              :     // SUBROUTINE INFORMATION:
     811              :     //       AUTHOR         Peter Graham Ellis
     812              :     //       DATE WRITTEN   March 2000
     813              :     //       MODIFIED       na
     814              :     //       RE-ENGINEERED  na
     815              : 
     816              :     // PURPOSE OF THIS SUBROUTINE:
     817              :     // This subroutine is for initializations of the ZonePlenum components.
     818              : 
     819              :     // METHODOLOGY EMPLOYED:
     820              :     // Similar to the Zone Splitter component but with interactions to the plenum zone.
     821              : 
     822              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     823              :     int InletNode;
     824              :     int OutletNode;
     825              :     int ZoneNodeNum;
     826              :     int NodeIndex;
     827              : 
     828              :     // Do the Begin Environment initializations
     829            2 :     if (state.dataZonePlenum->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
     830              : 
     831            0 :         for (int PlenumZoneNum = 1; PlenumZoneNum <= state.dataZonePlenum->NumZoneSupplyPlenums; ++PlenumZoneNum) {
     832              : 
     833            0 :             ZoneNodeNum = state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).ZoneNodeNum;
     834            0 :             auto &node = state.dataLoopNodes->Node(ZoneNodeNum);
     835            0 :             node.Temp = 20.0;
     836            0 :             node.MassFlowRate = 0.0;
     837            0 :             node.Quality = 1.0;
     838            0 :             node.Press = state.dataEnvrn->OutBaroPress;
     839            0 :             node.HumRat = state.dataEnvrn->OutHumRat;
     840            0 :             node.Enthalpy = PsyHFnTdbW(node.Temp, node.HumRat);
     841              : 
     842            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).ZoneTemp = 20.0;
     843            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).ZoneHumRat = 0.0;
     844            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).ZoneEnthalpy = 0.0;
     845            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletTemp = 0.0;
     846            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletHumRat = 0.0;
     847            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletEnthalpy = 0.0;
     848            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletPressure = 0.0;
     849            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletMassFlowRate = 0.0;
     850            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletMassFlowRateMaxAvail = 0.0;
     851            0 :             state.dataZonePlenum->ZoneSupPlenCond(PlenumZoneNum).InletMassFlowRateMinAvail = 0.0;
     852              :         }
     853              : 
     854            0 :         state.dataZonePlenum->MyEnvrnFlag = false;
     855              :     }
     856              : 
     857            2 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     858            2 :         state.dataZonePlenum->MyEnvrnFlag = true;
     859              :     }
     860              : 
     861              :     // Do the following initializations (every time step): This should be the info from
     862              :     // the previous components outlets or the node data in this section.
     863              : 
     864            2 :     InletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletNode;
     865            2 :     auto const &inletNode = state.dataLoopNodes->Node(InletNode);
     866            2 :     ZoneNodeNum = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneNodeNum;
     867            2 :     auto &zoneNode = state.dataLoopNodes->Node(ZoneNodeNum);
     868              : 
     869            2 :     if (FirstHVACIteration && FirstCall) {
     870            1 :         if (inletNode.MassFlowRate > 0.0) {
     871            0 :             zoneNode.MassFlowRate = inletNode.MassFlowRate;
     872            0 :             for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
     873            0 :                 OutletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletNode(NodeIndex);
     874            0 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate =
     875            0 :                     inletNode.MassFlowRate / state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes;
     876              :             }
     877              :         }
     878            1 :         if (inletNode.MassFlowRateMaxAvail > 0.0) {
     879            0 :             zoneNode.MassFlowRateMaxAvail = inletNode.MassFlowRateMaxAvail;
     880            0 :             for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
     881            0 :                 OutletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletNode(NodeIndex);
     882            0 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail =
     883            0 :                     inletNode.MassFlowRateMaxAvail / state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes;
     884              :             }
     885              :         }
     886              : 
     887              :     } // For FirstHVACIteration and FirstCall
     888              : 
     889            2 :     if (FirstCall) {
     890              : 
     891            1 :         if (inletNode.MassFlowRateMaxAvail == 0.0) { // For Node inlet Max Avail = 0.0
     892              : 
     893            2 :             for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
     894            1 :                 OutletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletNode(NodeIndex);
     895            1 :                 auto &outletNode = state.dataLoopNodes->Node(OutletNode);
     896            1 :                 outletNode.MassFlowRate = 0.0;
     897            1 :                 outletNode.MassFlowRateMaxAvail = 0.0;
     898            1 :                 outletNode.MassFlowRateMinAvail = 0.0;
     899              :             }
     900              : 
     901            1 :             zoneNode.MassFlowRate = 0.0;
     902            1 :             zoneNode.MassFlowRateMaxAvail = 0.0;
     903            1 :             zoneNode.MassFlowRateMinAvail = 0.0;
     904              : 
     905              :         } // For Node inlet Max Avail = 0.0
     906              : 
     907              :         // Add stuff to calculate conduction inputs to the zone plenum
     908              :         // Now load the zone conditions
     909            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneTemp = zoneNode.Temp;
     910            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneHumRat = zoneNode.HumRat;
     911            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneEnthalpy = zoneNode.Enthalpy;
     912              : 
     913            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
     914            1 :             OutletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletNode(NodeIndex);
     915            1 :             auto &outletNode = state.dataLoopNodes->Node(OutletNode);
     916            1 :             outletNode.Press = inletNode.Press;
     917            1 :             outletNode.Quality = inletNode.Quality;
     918              :         }
     919              : 
     920            1 :         zoneNode.Press = inletNode.Press;
     921            1 :         zoneNode.Quality = inletNode.Quality;
     922              : 
     923              :     } else { // On the second call from the ZoneEquipManager this is where the flows are passed back to
     924              :         // the supply plenum inlet.
     925            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
     926            1 :             OutletNode = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletNode(NodeIndex);
     927            1 :             auto const &outletNode = state.dataLoopNodes->Node(OutletNode);
     928            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRate(NodeIndex) = outletNode.MassFlowRate;
     929            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail(NodeIndex) = outletNode.MassFlowRateMaxAvail;
     930            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRateMinAvail(NodeIndex) = outletNode.MassFlowRateMinAvail;
     931              :         }
     932              : 
     933              :     } // For FirstCall
     934            2 : }
     935              : 
     936            8 : void CalcAirZoneReturnPlenum(EnergyPlusData &state, int const ZonePlenumNum)
     937              : {
     938              : 
     939              :     // SUBROUTINE INFORMATION:
     940              :     //       AUTHOR         Peter Graham Ellis
     941              :     //       DATE WRITTEN   November 2000
     942              :     //       MODIFIED       na
     943              :     //       RE-ENGINEERED  na
     944              : 
     945              :     // Using/Aliasing
     946              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     947            8 :     int InletNodeNum(0);            // inlet node number
     948            8 :     int IndNum(0);                  // induced air index
     949            8 :     int ADUListIndex(0);            // air distribution unit index in zone return plenum data structure
     950            8 :     Real64 TotIndMassFlowRate(0.0); // total induced air mass flow rate [kg/s]
     951              : 
     952              :     // Reset the totals to zero before they are summed.
     953            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate = 0.0;
     954            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail = 0.0;
     955            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMinAvail = 0.0;
     956            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletTemp = 0.0;
     957            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletHumRat = 0.0;
     958            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletPressure = 0.0;
     959            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletEnthalpy = 0.0;
     960            8 :     TotIndMassFlowRate = 0.0;
     961              : 
     962           33 :     for (InletNodeNum = 1; InletNodeNum <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumInletNodes; ++InletNodeNum) {
     963           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate +=
     964           25 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRate(InletNodeNum);
     965           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail +=
     966           25 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRateMaxAvail(InletNodeNum);
     967           25 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMinAvail +=
     968           25 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRateMinAvail(InletNodeNum);
     969              :     }
     970              : 
     971            8 :     if (state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate > 0.0) {
     972              : 
     973              :         // "Momentum balance" to get outlet air pressure
     974           20 :         for (InletNodeNum = 1; InletNodeNum <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumInletNodes; ++InletNodeNum) {
     975              : 
     976           16 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletPressure +=
     977           16 :                 state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletPressure(InletNodeNum) *
     978           16 :                 state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletMassFlowRate(InletNodeNum) /
     979           16 :                 state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate;
     980              :         }
     981              : 
     982              :     } else {
     983              :         // Mass Flow in air loop is zero and loop is not operating.
     984              :         // Arbitrarily set the output to the first inlet leg
     985            4 :         state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletPressure = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InletPressure(1);
     986              :     }
     987              : 
     988              :     // add in the leak flow rate, if any. Don't alter the pressure calc (it is not used anyway)
     989           32 :     for (ADUListIndex = 1; ADUListIndex <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumADUs; ++ADUListIndex) {
     990           24 :         int ADUNum = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ADUIndex(ADUListIndex);
     991           24 :         if (state.dataDefineEquipment->AirDistUnit(ADUNum).UpStreamLeak || state.dataDefineEquipment->AirDistUnit(ADUNum).DownStreamLeak) {
     992            0 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate +=
     993            0 :                 state.dataDefineEquipment->AirDistUnit(ADUNum).MassFlowRateUpStrLk +
     994            0 :                 state.dataDefineEquipment->AirDistUnit(ADUNum).MassFlowRateDnStrLk;
     995            0 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail +=
     996            0 :                 state.dataDefineEquipment->AirDistUnit(ADUNum).MaxAvailDelta;
     997            0 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMinAvail +=
     998            0 :                 state.dataDefineEquipment->AirDistUnit(ADUNum).MinAvailDelta;
     999              :         }
    1000              :     }
    1001              :     // Sum up induced air flow rate
    1002           18 :     for (IndNum = 1; IndNum <= state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).NumInducedNodes; ++IndNum) {
    1003           10 :         TotIndMassFlowRate += state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).InducedMassFlowRate(IndNum);
    1004              :     }
    1005              : 
    1006            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate -= TotIndMassFlowRate;
    1007              : 
    1008              :     // Set the Plenum Outlet to the Zone Node conditions
    1009            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletHumRat = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneHumRat;
    1010            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletEnthalpy = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneEnthalpy;
    1011            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletTemp = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).ZoneTemp;
    1012              :     // make sure the MassFlowMaxAvail >= MassFlowRate
    1013            8 :     state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail =
    1014            8 :         max(state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail,
    1015            8 :             state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum).OutletMassFlowRate);
    1016            8 : }
    1017              : 
    1018            2 : void CalcAirZoneSupplyPlenum(EnergyPlusData &state, int const ZonePlenumNum, bool const FirstCall)
    1019              : {
    1020              : 
    1021              :     // SUBROUTINE INFORMATION:
    1022              :     //       AUTHOR         Peter Graham Ellis
    1023              :     //       DATE WRITTEN   March 2000
    1024              :     //       MODIFIED       na
    1025              :     //       RE-ENGINEERED  na
    1026              : 
    1027              :     // METHODOLOGY EMPLOYED:
    1028              :     // Similar to the Zone Splitter component but with interactions to the plenum zone.
    1029              : 
    1030              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1031              :     int NodeIndex;
    1032              : 
    1033              :     // The first time through the State properties are passed through
    1034            2 :     if (FirstCall) {
    1035              :         // Moisture balance to get outlet air humidity ratio
    1036            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
    1037            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletHumRat(NodeIndex) =
    1038            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneHumRat;
    1039              :         }
    1040              : 
    1041              :         // Energy balance to get outlet air enthalpy
    1042            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
    1043            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletEnthalpy(NodeIndex) =
    1044            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneEnthalpy;
    1045              :         }
    1046              : 
    1047              :         // Set outlet temperatures equal to inlet temperature
    1048            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
    1049            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletTemp(NodeIndex) =
    1050            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).ZoneTemp;
    1051              :         }
    1052              : 
    1053              :     } else {
    1054              :         // This is the second time through and this is where the mass flows from the outlets are
    1055              :         // summed and then assigned upstream to the inlet node.
    1056            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRate = 0.0;
    1057            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRateMaxAvail = 0.0;
    1058            1 :         state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRateMinAvail = 0.0;
    1059            2 :         for (NodeIndex = 1; NodeIndex <= state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).NumOutletNodes; ++NodeIndex) {
    1060            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRate +=
    1061            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRate(NodeIndex);
    1062            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRateMaxAvail +=
    1063            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRateMaxAvail(NodeIndex);
    1064            1 :             state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).InletMassFlowRateMinAvail +=
    1065            1 :                 state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum).OutletMassFlowRateMinAvail(NodeIndex);
    1066              :         }
    1067              :     }
    1068            2 : }
    1069              : 
    1070              : // End Algorithm Section of the Module
    1071              : // *****************************************************************************
    1072              : 
    1073              : // Beginning of Update subroutines for the ZonePlenum Module
    1074              : // *****************************************************************************
    1075              : 
    1076            9 : void UpdateAirZoneReturnPlenum(EnergyPlusData &state, int const ZonePlenumNum)
    1077              : {
    1078              : 
    1079              :     // SUBROUTINE INFORMATION:
    1080              :     //       AUTHOR         Peter Graham Ellis
    1081              :     //       DATE WRITTEN   November 2000
    1082              : 
    1083              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1084            9 :     auto const &zoneRetPlenCond = state.dataZonePlenum->ZoneRetPlenCond(ZonePlenumNum);
    1085            9 :     auto &outletNode = state.dataLoopNodes->Node(zoneRetPlenCond.OutletNode);
    1086            9 :     auto const &inletNode = state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(1));
    1087            9 :     auto &zoneNode = state.dataLoopNodes->Node(zoneRetPlenCond.ZoneNodeNum);
    1088              : 
    1089              :     // Set the outlet air nodes of the ZonePlenum
    1090            9 :     outletNode.MassFlowRate = zoneRetPlenCond.OutletMassFlowRate;
    1091            9 :     outletNode.MassFlowRateMaxAvail = zoneRetPlenCond.OutletMassFlowRateMaxAvail;
    1092            9 :     outletNode.MassFlowRateMinAvail = zoneRetPlenCond.OutletMassFlowRateMinAvail;
    1093              : 
    1094            9 :     zoneNode.MassFlowRate = zoneRetPlenCond.OutletMassFlowRate;
    1095            9 :     zoneNode.MassFlowRateMaxAvail = zoneRetPlenCond.OutletMassFlowRateMaxAvail;
    1096            9 :     zoneNode.MassFlowRateMinAvail = zoneRetPlenCond.OutletMassFlowRateMinAvail;
    1097            9 :     zoneNode.Press = zoneRetPlenCond.OutletPressure;
    1098              : 
    1099            9 :     outletNode.Temp = zoneRetPlenCond.OutletTemp;
    1100            9 :     outletNode.HumRat = zoneRetPlenCond.OutletHumRat;
    1101            9 :     outletNode.Enthalpy = zoneRetPlenCond.OutletEnthalpy;
    1102            9 :     outletNode.Press = zoneRetPlenCond.OutletPressure;
    1103           21 :     for (int IndNum = 1; IndNum <= zoneRetPlenCond.NumInducedNodes; ++IndNum) {
    1104           12 :         int InducedNode = zoneRetPlenCond.InducedNode(IndNum);
    1105           12 :         auto &inducedNode = state.dataLoopNodes->Node(InducedNode);
    1106           12 :         inducedNode.Temp = zoneRetPlenCond.InducedTemp(IndNum);
    1107           12 :         inducedNode.HumRat = zoneRetPlenCond.InducedHumRat(IndNum);
    1108           12 :         inducedNode.Enthalpy = zoneRetPlenCond.InducedEnthalpy(IndNum);
    1109           12 :         inducedNode.Press = zoneRetPlenCond.InducedPressure(IndNum);
    1110           12 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1111            2 :             inducedNode.CO2 = zoneRetPlenCond.InducedCO2(IndNum);
    1112              :         }
    1113           12 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1114            2 :             inducedNode.GenContam = zoneRetPlenCond.InducedGenContam(IndNum);
    1115              :         }
    1116           12 :         inducedNode.Quality = inletNode.Quality;
    1117              :     }
    1118              : 
    1119              :     // Set the outlet nodes for properties that are just pass through and not used
    1120            9 :     outletNode.Quality = inletNode.Quality;
    1121            9 :     zoneNode.Quality = inletNode.Quality;
    1122              : 
    1123              :     // Set the outlet node contaminant properties if needed. The zone contaminant conditions are calculated in ZoneContaminantPredictorCorrector
    1124            9 :     if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1125            1 :         if (zoneRetPlenCond.OutletMassFlowRate > 0.0) {
    1126              :             // CO2 balance to get outlet air CO2
    1127            0 :             outletNode.CO2 = 0.0;
    1128            0 :             for (int InletNodeNum = 1; InletNodeNum <= zoneRetPlenCond.NumInletNodes; ++InletNodeNum) {
    1129            0 :                 outletNode.CO2 += state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(InletNodeNum)).CO2 *
    1130            0 :                                   zoneRetPlenCond.InletMassFlowRate(InletNodeNum) / zoneRetPlenCond.OutletMassFlowRate;
    1131              :             }
    1132            0 :             zoneNode.CO2 = outletNode.CO2;
    1133              :         } else {
    1134            1 :             outletNode.CO2 = zoneNode.CO2;
    1135              :         }
    1136              :     }
    1137            9 :     if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1138            1 :         if (zoneRetPlenCond.OutletMassFlowRate > 0.0) {
    1139              :             // GenContam balance to get outlet air GenContam
    1140            0 :             outletNode.GenContam = 0.0;
    1141            0 :             for (int InletNodeNum = 1; InletNodeNum <= zoneRetPlenCond.NumInletNodes; ++InletNodeNum) {
    1142            0 :                 outletNode.GenContam += state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(InletNodeNum)).GenContam *
    1143            0 :                                         zoneRetPlenCond.InletMassFlowRate(InletNodeNum) / zoneRetPlenCond.OutletMassFlowRate;
    1144              :             }
    1145            0 :             zoneNode.GenContam = outletNode.GenContam;
    1146              :         } else {
    1147            1 :             outletNode.GenContam = zoneNode.GenContam;
    1148              :         }
    1149              :     }
    1150            9 : }
    1151              : 
    1152            2 : void UpdateAirZoneSupplyPlenum(EnergyPlusData &state, int const ZonePlenumNum, bool &PlenumInletChanged, bool const FirstCall)
    1153              : {
    1154              : 
    1155              :     // SUBROUTINE INFORMATION:
    1156              :     //       AUTHOR         Peter Graham Ellis
    1157              :     //       DATE WRITTEN   March 2000
    1158              : 
    1159              :     // METHODOLOGY EMPLOYED:
    1160              :     // Similar to the Zone Splitter component but with interactions to the plenum zone.
    1161              : 
    1162              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1163            2 :     Real64 constexpr FlowRateToler = 0.01; // Tolerance for mass flow rate convergence (in kg/s)
    1164              : 
    1165              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1166            2 :     auto const &zoneSupPlenCon = state.dataZonePlenum->ZoneSupPlenCond(ZonePlenumNum);
    1167            2 :     auto &inletNode = state.dataLoopNodes->Node(zoneSupPlenCon.InletNode);
    1168            2 :     auto &zoneNode = state.dataLoopNodes->Node(zoneSupPlenCon.ZoneNodeNum);
    1169              : 
    1170              :     // On the FirstCall the State properties are passed through and the mass flows are not dealt with
    1171            2 :     if (FirstCall) {
    1172              :         // Set the outlet nodes for properties that just pass through and not used
    1173            2 :         for (int NodeIndex = 1; NodeIndex <= zoneSupPlenCon.NumOutletNodes; ++NodeIndex) {
    1174            1 :             int OutletNode = zoneSupPlenCon.OutletNode(NodeIndex);
    1175            1 :             auto &outletNode = state.dataLoopNodes->Node(OutletNode);
    1176            1 :             outletNode.Temp = zoneSupPlenCon.OutletTemp(NodeIndex);
    1177            1 :             outletNode.HumRat = zoneSupPlenCon.OutletHumRat(NodeIndex);
    1178            1 :             outletNode.Enthalpy = zoneSupPlenCon.OutletEnthalpy(NodeIndex);
    1179            1 :             if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1180            0 :                 outletNode.CO2 = inletNode.CO2;
    1181              :             }
    1182            1 :             if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1183            0 :                 outletNode.GenContam = inletNode.GenContam;
    1184              :             }
    1185              :         }
    1186              : 
    1187            1 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1188            0 :             zoneNode.CO2 = inletNode.CO2;
    1189              :         }
    1190            1 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1191            0 :             zoneNode.GenContam = inletNode.GenContam;
    1192              :         }
    1193              : 
    1194              :     } else {
    1195              :         // The second time through just updates the mass flow conditions back upstream to the inlet.
    1196            1 :         if (std::abs(inletNode.MassFlowRate - zoneSupPlenCon.InletMassFlowRate) > FlowRateToler) {
    1197            0 :             PlenumInletChanged = true;
    1198              :         }
    1199              : 
    1200            1 :         inletNode.MassFlowRate = zoneSupPlenCon.InletMassFlowRate;
    1201            1 :         inletNode.MassFlowRateMaxAvail = zoneSupPlenCon.InletMassFlowRateMaxAvail;
    1202            1 :         inletNode.MassFlowRateMinAvail = zoneSupPlenCon.InletMassFlowRateMinAvail;
    1203              : 
    1204            1 :         zoneNode.MassFlowRate = zoneSupPlenCon.InletMassFlowRate;
    1205            1 :         zoneNode.MassFlowRateMaxAvail = zoneSupPlenCon.InletMassFlowRateMaxAvail;
    1206            1 :         zoneNode.MassFlowRateMinAvail = zoneSupPlenCon.InletMassFlowRateMinAvail;
    1207              : 
    1208              :     } // For FirstCall
    1209            2 : }
    1210              : 
    1211            1 : int GetReturnPlenumIndex(EnergyPlusData &state, int const ExNodeNum)
    1212              : {
    1213              : 
    1214              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1215              :     int WhichPlenum; // index to return plenum
    1216              : 
    1217              :     // Obtains and Allocates ZonePlenum related parameters from input file
    1218            1 :     if (state.dataZonePlenum->GetInputFlag) { // First time subroutine has been entered
    1219            1 :         GetZonePlenumInput(state);
    1220            1 :         state.dataZonePlenum->GetInputFlag = false;
    1221              :     }
    1222              : 
    1223            1 :     WhichPlenum = 0;
    1224            1 :     if (state.dataZonePlenum->NumZoneReturnPlenums > 0) {
    1225            1 :         for (int PlenumNum = 1; PlenumNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++PlenumNum) {
    1226            1 :             if (ExNodeNum != state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).OutletNode) continue;
    1227            1 :             WhichPlenum = PlenumNum;
    1228            1 :             break;
    1229              :         }
    1230            1 :         if (WhichPlenum == 0) {
    1231            0 :             for (int PlenumNum = 1; PlenumNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++PlenumNum) {
    1232            0 :                 for (int InducedNodeNum = 1; InducedNodeNum <= state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).NumInducedNodes; ++InducedNodeNum) {
    1233            0 :                     if (ExNodeNum != state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).InducedNode(InducedNodeNum)) continue;
    1234            0 :                     WhichPlenum = PlenumNum;
    1235            0 :                     break;
    1236              :                 }
    1237            0 :                 if (WhichPlenum > 0) break;
    1238              :             }
    1239              :         }
    1240              :     }
    1241              : 
    1242            1 :     return WhichPlenum;
    1243              : }
    1244              : 
    1245            1 : void GetReturnPlenumName(EnergyPlusData &state, int const ReturnPlenumIndex, std::string &ReturnPlenumName)
    1246              : {
    1247              : 
    1248              :     // Obtains and Allocates ZonePlenum related parameters from input file
    1249            1 :     if (state.dataZonePlenum->GetInputFlag) { // First time subroutine has been entered
    1250            0 :         GetZonePlenumInput(state);
    1251            0 :         state.dataZonePlenum->GetInputFlag = false;
    1252              :     }
    1253              : 
    1254            1 :     ReturnPlenumName = " ";
    1255            1 :     if (state.dataZonePlenum->NumZoneReturnPlenums > 0) {
    1256            1 :         ReturnPlenumName = state.dataZonePlenum->ZoneRetPlenCond(ReturnPlenumIndex).ZonePlenumName;
    1257              :     }
    1258            1 : }
    1259              : 
    1260            2 : int getReturnPlenumIndexFromInletNode(EnergyPlusData &state, int const InNodeNum)
    1261              : {
    1262              : 
    1263              :     // Obtains and Allocates ZonePlenum related parameters from input file
    1264            2 :     if (state.dataZonePlenum->GetInputFlag) { // First time subroutine has been entered
    1265            2 :         GetZonePlenumInput(state);
    1266            2 :         state.dataZonePlenum->GetInputFlag = false;
    1267              :     }
    1268              : 
    1269            2 :     int thisPlenum = 0;
    1270            2 :     if (state.dataZonePlenum->NumZoneReturnPlenums > 0) {
    1271            0 :         for (int PlenumNum = 1; PlenumNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++PlenumNum) {
    1272            0 :             for (int InNodeCtr = 1; InNodeCtr <= state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).NumInletNodes; ++InNodeCtr) {
    1273            0 :                 if (InNodeNum != state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).InletNode(InNodeCtr)) continue;
    1274            0 :                 thisPlenum = PlenumNum;
    1275            0 :                 break;
    1276              :             }
    1277            0 :             if (thisPlenum > 0) break;
    1278              :         }
    1279              :     }
    1280              : 
    1281            2 :     return thisPlenum;
    1282              : }
    1283              : 
    1284            5 : bool ValidateInducedNode(EnergyPlusData &state, int const InduceNodeNum, int const NumReturnNodes, Array1D<int> const &ReturnNode)
    1285              : {
    1286              :     // Ensure induced node is used as inlet node of zoe equipment
    1287            5 :     bool Nodefound = false;
    1288              : 
    1289              :     // Obtains and Allocates ZonePlenum related parameters from input file
    1290            5 :     if (state.dataZonePlenum->GetInputFlag) { // First time subroutine has been entered
    1291            2 :         GetZonePlenumInput(state);
    1292            2 :         state.dataZonePlenum->GetInputFlag = false;
    1293              :     }
    1294              : 
    1295            5 :     if (state.dataZonePlenum->NumZoneReturnPlenums > 0) {
    1296            6 :         for (int PlenumNum = 1; PlenumNum <= state.dataZonePlenum->NumZoneReturnPlenums; ++PlenumNum) {
    1297            9 :             for (int InduceNodeCtr = 1; InduceNodeCtr <= state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).NumInducedNodes; ++InduceNodeCtr) {
    1298            8 :                 if (InduceNodeNum == state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).InducedNode(InduceNodeCtr)) {
    1299           12 :                     for (int InNodeCtr = 1; InNodeCtr <= state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).NumInletNodes; ++InNodeCtr) {
    1300           20 :                         for (int ReturnNodeNum = 1; ReturnNodeNum <= NumReturnNodes; ++ReturnNodeNum) {
    1301           12 :                             if (ReturnNode(ReturnNodeNum) != state.dataZonePlenum->ZoneRetPlenCond(PlenumNum).InletNode(InNodeCtr)) continue;
    1302            4 :                             Nodefound = true;
    1303            4 :                             break;
    1304              :                         }
    1305           12 :                         if (Nodefound) break;
    1306              :                     }
    1307              :                 }
    1308            8 :                 if (Nodefound) break;
    1309              :             }
    1310            5 :             if (Nodefound) break;
    1311              :         }
    1312              :     }
    1313              : 
    1314            5 :     return Nodefound;
    1315              : }
    1316              : 
    1317              : } // namespace EnergyPlus::ZonePlenum
        

Generated by: LCOV version 2.0-1