LCOV - code coverage report
Current view: top level - EnergyPlus - PurchasedAirManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 53.7 % 1755 943
Test Date: 2025-05-22 16:09:37 Functions: 100.0 % 17 17

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Array.functions.hh>
      53              : // #include <ObjexxFCL/Fmath.hh>
      54              : #include <ObjexxFCL/string.functions.hh>
      55              : 
      56              : // EnergyPlus Headers
      57              : #include <EnergyPlus/Autosizing/CoolingAirFlowSizing.hh>
      58              : #include <EnergyPlus/Autosizing/CoolingCapacitySizing.hh>
      59              : #include <EnergyPlus/Autosizing/HeatingAirFlowSizing.hh>
      60              : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
      61              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      62              : #include <EnergyPlus/DataContaminantBalance.hh>
      63              : #include <EnergyPlus/DataEnvironment.hh>
      64              : #include <EnergyPlus/DataHVACGlobals.hh>
      65              : #include <EnergyPlus/DataHeatBalFanSys.hh>
      66              : #include <EnergyPlus/DataHeatBalance.hh>
      67              : #include <EnergyPlus/DataIPShortCuts.hh>
      68              : #include <EnergyPlus/DataLoopNode.hh>
      69              : #include <EnergyPlus/DataSizing.hh>
      70              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      71              : #include <EnergyPlus/DataZoneEquipment.hh>
      72              : #include <EnergyPlus/EMSManager.hh>
      73              : #include <EnergyPlus/General.hh>
      74              : #include <EnergyPlus/GeneralRoutines.hh>
      75              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      76              : #include <EnergyPlus/NodeInputManager.hh>
      77              : #include <EnergyPlus/OutAirNodeManager.hh>
      78              : #include <EnergyPlus/OutputProcessor.hh>
      79              : #include <EnergyPlus/Psychrometrics.hh>
      80              : #include <EnergyPlus/PurchasedAirManager.hh>
      81              : #include <EnergyPlus/ScheduleManager.hh>
      82              : #include <EnergyPlus/UtilityRoutines.hh>
      83              : #include <EnergyPlus/ZonePlenum.hh>
      84              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      85              : 
      86              : namespace EnergyPlus::PurchasedAirManager {
      87              : 
      88              : // Module containing data and routines dealing with Ideal Loads Air System (formerly PURCHASED AIR).
      89              : 
      90              : // MODULE INFORMATION:
      91              : //       AUTHOR         Russ Taylor
      92              : //       DATE WRITTEN   May 1997
      93              : //       MODIFIED       Fred Buhl Dec 1999
      94              : //                      B. Griffith Dec 2006. added OA lookup function, moved getinputflag up to Module
      95              : //                      M. Witte June 2011, add new features including DCV, economizer, dehumidification and humidification
      96              : //                      NOTE: MJW Sep 13, 2011:  Still need to review checks for negative loads and impossible supply temps???
      97              : //                           There are no Deallocate statements in here - should there be?
      98              : //       RE-ENGINEERED  na
      99              : 
     100              : // PURPOSE OF THIS MODULE:
     101              : // To encapsulate the data and algorithms required to simulate the
     102              : // Zone Ideal Loads Air System component. This component supplies hot or cold air
     103              : // at a fixed or variable temperature to a zone to meet the zone load.
     104              : // With the June 2011 enhancements it will also supply outdoor air with optional demand-controlled ventilation
     105              : // and economizer controls, plus new options for controlling zone humidity.
     106              : 
     107              : // METHODOLOGY EMPLOYED:
     108              : // The user can choose via input the max/min hot and cold supply air
     109              : // temperature and humidity ratio. The air mass flow rate is chosen
     110              : // to meet the (remaining) zone load or based on the outdoor air flow requirement.
     111              : // If the outdoor air flow sets the flow rate, the supply air temperature and
     112              : // humidity ratio are adjusted to meet the zone load.
     113              : 
     114              : // Using/Aliasing
     115              : using Psychrometrics::PsyCpAirFnW;
     116              : using Psychrometrics::PsyHFnTdbW;
     117              : using Psychrometrics::PsyRhoAirFnPbTdbW;
     118              : using Psychrometrics::PsyTdbFnHW;
     119              : using Psychrometrics::PsyTsatFnHPb;
     120              : using Psychrometrics::PsyWFnTdbH;
     121              : using Psychrometrics::PsyWFnTdbRhPb;
     122              : 
     123              : // Delta humidity ratio limit, 0.00025 equals delta between 45F dewpoint and 46F dewpoint
     124              : // used to prevent dividing by near zero
     125              : Real64 constexpr SmallDeltaHumRat(0.00025);
     126              : 
     127              : constexpr std::array<std::string_view, (int)LimitType::Num> limitTypeNames = {
     128              :     "NoLimit", "LimitFlowRate", "LimitCapacity", "LimitFlowRateAndCapacity"};
     129              : constexpr std::array<std::string_view, (int)LimitType::Num> limitTypeNamesUC = {
     130              :     "NOLIMIT", "LIMITFLOWRATE", "LIMITCAPACITY", "LIMITFLOWRATEANDCAPACITY"};
     131              : 
     132              : constexpr std::array<std::string_view, (int)HumControl::Num> humControlNames = {
     133              :     "None", "ConstantSensibleHeatRatio", "Humidistat", "ConstantSupplyHumidityRatio"};
     134              : constexpr std::array<std::string_view, (int)HumControl::Num> humControlNamesUC = {
     135              :     "NONE", "CONSTANTSENSIBLEHEATRATIO", "HUMIDISTAT", "CONSTANTSUPPLYHUMIDITYRATIO"};
     136              : 
     137              : constexpr std::array<std::string_view, (int)DCV::Num> dcvNames = {"None", "OccupancySchedule", "CO2SetPoint"};
     138              : constexpr std::array<std::string_view, (int)DCV::Num> dcvNamesUC = {"NONE", "OCCUPANCYSCHEDULE", "CO2SETPOINT"};
     139              : 
     140              : constexpr std::array<std::string_view, (int)Econ::Num> econNames = {"NoEconomizer", "DifferentialDryBulb", "DifferentialEnthalpy"};
     141              : constexpr std::array<std::string_view, (int)Econ::Num> econNamesUC = {"NOECONOMIZER", "DIFFERENTIALDRYBULB", "DIFFERENTIALENTHALPY"};
     142              : 
     143              : constexpr std::array<std::string_view, (int)HeatRecovery::Num> heatRecoveryNames = {"None", "Sensible", "Enthalpy"};
     144              : constexpr std::array<std::string_view, (int)HeatRecovery::Num> heatRecoveryNamesUC = {"NONE", "SENSIBLE", "ENTHALPY"};
     145              : 
     146          761 : void SimPurchasedAir(EnergyPlusData &state,
     147              :                      std::string const &PurchAirName,
     148              :                      Real64 &SysOutputProvided,
     149              :                      Real64 &MoistOutputProvided, // Moisture output provided (kg/s), dehumidification = negative
     150              :                      bool const FirstHVACIteration,
     151              :                      int const ControlledZoneNum,
     152              :                      int &CompIndex)
     153              : {
     154              : 
     155              :     // SUBROUTINE INFORMATION:
     156              :     //       AUTHOR         Russ Taylor
     157              :     //       DATE WRITTEN   May 1997
     158              :     //       MODIFIED       Don Shirey, Aug 2009 (LatOutputProvided - now MoistOutputProvided)
     159              :     //       RE-ENGINEERED  na
     160              : 
     161              :     // PURPOSE OF THIS SUBROUTINE:
     162              :     // This subroutine manages Purchased Air component simulation.
     163              :     // It is called from SimZoneEquipment in the ZoneEquipmentManager
     164              :     // at the system time step.
     165              : 
     166              :     int PurchAirNum;
     167              : 
     168          761 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
     169            9 :         GetPurchasedAir(state);
     170            9 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
     171              :     }
     172              : 
     173              :     // Find the correct PurchasedAir Equipment
     174          761 :     if (CompIndex == 0) {
     175            9 :         PurchAirNum = Util::FindItemInList(PurchAirName, state.dataPurchasedAirMgr->PurchAir);
     176            9 :         if (PurchAirNum == 0) {
     177            0 :             ShowFatalError(state, format("SimPurchasedAir: Unit not found={}", PurchAirName));
     178              :         }
     179            9 :         CompIndex = PurchAirNum;
     180              :     } else {
     181          752 :         PurchAirNum = CompIndex;
     182          752 :         if (PurchAirNum > state.dataPurchasedAirMgr->NumPurchAir || PurchAirNum < 1) {
     183            0 :             ShowFatalError(state,
     184            0 :                            format("SimPurchasedAir:  Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
     185              :                                   PurchAirNum,
     186            0 :                                   state.dataPurchasedAirMgr->NumPurchAir,
     187              :                                   PurchAirName));
     188              :         }
     189          752 :         if (state.dataPurchasedAirMgr->CheckEquipName(PurchAirNum)) {
     190            5 :             if (PurchAirName != state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name) {
     191            0 :                 ShowFatalError(state,
     192            0 :                                format("SimPurchasedAir: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
     193              :                                       PurchAirNum,
     194              :                                       PurchAirName,
     195            0 :                                       state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name));
     196              :             }
     197            5 :             state.dataPurchasedAirMgr->CheckEquipName(PurchAirNum) = false;
     198              :         }
     199              :     }
     200              : 
     201          761 :     InitPurchasedAir(state, PurchAirNum, ControlledZoneNum);
     202              : 
     203          761 :     CalcPurchAirLoads(state, PurchAirNum, SysOutputProvided, MoistOutputProvided, ControlledZoneNum);
     204              : 
     205          761 :     UpdatePurchasedAir(state, PurchAirNum, FirstHVACIteration);
     206              : 
     207          761 :     ReportPurchasedAir(state, PurchAirNum);
     208          761 : }
     209              : 
     210           17 : void GetPurchasedAir(EnergyPlusData &state)
     211              : {
     212              : 
     213              :     // SUBROUTINE INFORMATION:
     214              :     //       AUTHOR         Russ Taylor
     215              :     //       DATE WRITTEN   June 1997
     216              :     //       MODIFIED       M. Witte, June 2011, add new features including DCV, economizer, dehumidification
     217              :     //                                           and humidification controls
     218              :     //       RE-ENGINEERED  na
     219              : 
     220              :     // PURPOSE OF THIS SUBROUTINE:
     221              :     // Get the input data for the Purchased Air objects.
     222              :     // Set up output variables.
     223              : 
     224              :     // Using/Aliasing
     225              :     using NodeInputManager::CheckUniqueNodeNames;
     226              :     using NodeInputManager::EndUniqueNodeCheck;
     227              :     using NodeInputManager::GetOnlySingleNode;
     228              :     using NodeInputManager::InitUniqueNodeCheck;
     229              :     using OutAirNodeManager::CheckAndAddAirNodeNumber;
     230              :     using namespace DataLoopNode;
     231              :     using ZonePlenum::GetReturnPlenumIndex;
     232              : 
     233              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     234              :     static constexpr std::string_view RoutineName("GetPurchasedAir: "); // include trailing blank space
     235              :     static constexpr std::string_view routineName = "GetPurchasedAir";
     236              : 
     237           17 :     bool ErrorsFound(false); // If errors detected in input
     238           17 :     auto &s_ipsc = state.dataIPShortCut;
     239              : 
     240           17 :     s_ipsc->cCurrentModuleObject = "ZoneHVAC:IdealLoadsAirSystem";
     241              : 
     242           17 :     auto &PurchAir(state.dataPurchasedAirMgr->PurchAir);
     243              : 
     244           17 :     state.dataPurchasedAirMgr->NumPurchAir = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     245              : 
     246           17 :     PurchAir.allocate(state.dataPurchasedAirMgr->NumPurchAir);
     247           17 :     state.dataPurchasedAirMgr->CheckEquipName.allocate(state.dataPurchasedAirMgr->NumPurchAir);
     248           17 :     state.dataPurchasedAirMgr->PurchAirNumericFields.allocate(state.dataPurchasedAirMgr->NumPurchAir);
     249           17 :     state.dataPurchasedAirMgr->CheckEquipName = true;
     250              : 
     251           17 :     if (state.dataPurchasedAirMgr->NumPurchAir > 0) {
     252              :         int NumAlphas;
     253              :         int NumNums;
     254              :         int IOStat;
     255           14 :         InitUniqueNodeCheck(state, s_ipsc->cCurrentModuleObject);
     256           28 :         for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
     257           14 :             PurchAir(PurchAirNum).cObjectName = s_ipsc->cCurrentModuleObject;
     258              : 
     259           28 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     260           14 :                                                                      s_ipsc->cCurrentModuleObject,
     261              :                                                                      PurchAirNum,
     262           14 :                                                                      s_ipsc->cAlphaArgs,
     263              :                                                                      NumAlphas,
     264           14 :                                                                      s_ipsc->rNumericArgs,
     265              :                                                                      NumNums,
     266              :                                                                      IOStat,
     267           14 :                                                                      s_ipsc->lNumericFieldBlanks,
     268           14 :                                                                      s_ipsc->lAlphaFieldBlanks,
     269           14 :                                                                      s_ipsc->cAlphaFieldNames,
     270           14 :                                                                      s_ipsc->cNumericFieldNames);
     271              : 
     272           14 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     273              : 
     274           14 :             state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames.allocate(NumNums);
     275           14 :             state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames = "";
     276           14 :             state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames = s_ipsc->cNumericFieldNames;
     277              : 
     278           14 :             PurchAir(PurchAirNum).Name = s_ipsc->cAlphaArgs(1);
     279              :             // get optional  availability schedule
     280           14 :             if (s_ipsc->lAlphaFieldBlanks(2)) {
     281           14 :                 PurchAir(PurchAirNum).availSched = Sched::GetScheduleAlwaysOn(state);
     282            0 :             } else if ((PurchAir(PurchAirNum).availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     283            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     284            0 :                 ErrorsFound = true;
     285              :             }
     286              :             // Purchased air supply air node is an outlet node
     287           28 :             PurchAir(PurchAirNum).ZoneSupplyAirNodeNum = GetOnlySingleNode(state,
     288           14 :                                                                            s_ipsc->cAlphaArgs(3),
     289              :                                                                            ErrorsFound,
     290              :                                                                            DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
     291           14 :                                                                            s_ipsc->cAlphaArgs(1),
     292              :                                                                            DataLoopNode::NodeFluidType::Air,
     293              :                                                                            DataLoopNode::ConnectionType::Outlet,
     294              :                                                                            NodeInputManager::CompFluidStream::Primary,
     295              :                                                                            ObjectIsNotParent);
     296           14 :             bool UniqueNodeError = false;
     297           14 :             CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(3), UniqueNodeError, s_ipsc->cAlphaArgs(3), s_ipsc->cAlphaArgs(1));
     298           14 :             if (UniqueNodeError) ErrorsFound = true;
     299              :             // If new (optional) exhaust air node name is present, then register it as inlet
     300           14 :             if (!s_ipsc->lAlphaFieldBlanks(4)) {
     301            8 :                 if (s_ipsc->lAlphaFieldBlanks(5)) {
     302           14 :                     PurchAir(PurchAirNum).ZoneExhaustAirNodeNum = GetOnlySingleNode(state,
     303            7 :                                                                                     s_ipsc->cAlphaArgs(4),
     304              :                                                                                     ErrorsFound,
     305              :                                                                                     DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
     306            7 :                                                                                     s_ipsc->cAlphaArgs(1),
     307              :                                                                                     DataLoopNode::NodeFluidType::Air,
     308              :                                                                                     DataLoopNode::ConnectionType::Inlet,
     309              :                                                                                     NodeInputManager::CompFluidStream::Primary,
     310              :                                                                                     ObjectIsNotParent);
     311              :                 } else {
     312            2 :                     PurchAir(PurchAirNum).ZoneExhaustAirNodeNum = GetOnlySingleNode(state,
     313            1 :                                                                                     s_ipsc->cAlphaArgs(4),
     314              :                                                                                     ErrorsFound,
     315              :                                                                                     DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
     316            1 :                                                                                     s_ipsc->cAlphaArgs(1),
     317              :                                                                                     DataLoopNode::NodeFluidType::Air,
     318              :                                                                                     DataLoopNode::ConnectionType::Outlet,
     319              :                                                                                     NodeInputManager::CompFluidStream::Primary,
     320              :                                                                                     ObjectIsNotParent);
     321              :                 }
     322            8 :                 UniqueNodeError = false;
     323            8 :                 CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(4), UniqueNodeError, s_ipsc->cAlphaArgs(4), s_ipsc->cAlphaArgs(1));
     324            8 :                 if (UniqueNodeError) ErrorsFound = true;
     325              :             }
     326           14 :             if (!s_ipsc->lAlphaFieldBlanks(5)) {
     327            2 :                 PurchAir(PurchAirNum).PlenumExhaustAirNodeNum = GetOnlySingleNode(state,
     328            1 :                                                                                   s_ipsc->cAlphaArgs(5),
     329              :                                                                                   ErrorsFound,
     330              :                                                                                   DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
     331            1 :                                                                                   s_ipsc->cAlphaArgs(1),
     332              :                                                                                   DataLoopNode::NodeFluidType::Air,
     333              :                                                                                   DataLoopNode::ConnectionType::Inlet,
     334              :                                                                                   NodeInputManager::CompFluidStream::Primary,
     335              :                                                                                   ObjectIsNotParent);
     336              :             }
     337           14 :             PurchAir(PurchAirNum).MaxHeatSuppAirTemp = s_ipsc->rNumericArgs(1);
     338           14 :             PurchAir(PurchAirNum).MinCoolSuppAirTemp = s_ipsc->rNumericArgs(2);
     339           14 :             PurchAir(PurchAirNum).MaxHeatSuppAirHumRat = s_ipsc->rNumericArgs(3);
     340           14 :             PurchAir(PurchAirNum).MinCoolSuppAirHumRat = s_ipsc->rNumericArgs(4);
     341              : 
     342           14 :             PurchAir(PurchAirNum).HeatingLimit = static_cast<LimitType>(getEnumValue(limitTypeNamesUC, s_ipsc->cAlphaArgs(6)));
     343           14 :             switch (PurchAir(PurchAirNum).HeatingLimit) {
     344              : 
     345           13 :             case LimitType::None: {
     346           13 :             } break;
     347              : 
     348            0 :             case LimitType::FlowRate: {
     349            0 :                 if (s_ipsc->lNumericFieldBlanks(5)) {
     350            0 :                     PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
     351              :                 }
     352            0 :             } break;
     353              : 
     354            1 :             case LimitType::Capacity: {
     355            1 :                 if (s_ipsc->lNumericFieldBlanks(6)) {
     356            0 :                     PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
     357              :                 }
     358            1 :             } break;
     359              : 
     360            0 :             case LimitType::FlowRateAndCapacity: {
     361            0 :                 if (s_ipsc->lNumericFieldBlanks(5) && s_ipsc->lNumericFieldBlanks(6)) {
     362            0 :                     PurchAir(PurchAirNum).HeatingLimit = LimitType::None;
     363            0 :                 } else if (s_ipsc->lNumericFieldBlanks(5)) {
     364            0 :                     PurchAir(PurchAirNum).HeatingLimit = LimitType::Capacity;
     365            0 :                 } else if (s_ipsc->lNumericFieldBlanks(6)) {
     366            0 :                     PurchAir(PurchAirNum).HeatingLimit = LimitType::FlowRate;
     367              :                 }
     368            0 :             } break;
     369              : 
     370            0 :             default: {
     371            0 :                 ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     372            0 :                 ErrorsFound = true;
     373            0 :             } break;
     374              :             }
     375              : 
     376           14 :             PurchAir(PurchAirNum).MaxHeatVolFlowRate = s_ipsc->rNumericArgs(5);
     377           14 :             PurchAir(PurchAirNum).MaxHeatSensCap = s_ipsc->rNumericArgs(6);
     378              : 
     379           14 :             PurchAir(PurchAirNum).CoolingLimit = static_cast<LimitType>(getEnumValue(limitTypeNamesUC, s_ipsc->cAlphaArgs(7)));
     380              : 
     381           14 :             switch (PurchAir(PurchAirNum).CoolingLimit) {
     382           14 :             case LimitType::None: {
     383           14 :             } break;
     384              : 
     385            0 :             case LimitType::FlowRate: {
     386            0 :                 if (s_ipsc->lNumericFieldBlanks(7)) {
     387            0 :                     PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
     388              :                 }
     389            0 :             } break;
     390              : 
     391            0 :             case LimitType::Capacity: {
     392            0 :                 if (s_ipsc->lNumericFieldBlanks(8)) {
     393            0 :                     PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
     394              :                 }
     395            0 :             } break;
     396              : 
     397            0 :             case LimitType::FlowRateAndCapacity: {
     398            0 :                 if (s_ipsc->lNumericFieldBlanks(7) && s_ipsc->lNumericFieldBlanks(8)) {
     399            0 :                     PurchAir(PurchAirNum).CoolingLimit = LimitType::None;
     400            0 :                 } else if (s_ipsc->lNumericFieldBlanks(7)) {
     401            0 :                     PurchAir(PurchAirNum).CoolingLimit = LimitType::Capacity;
     402            0 :                 } else if (s_ipsc->lNumericFieldBlanks(8)) {
     403            0 :                     PurchAir(PurchAirNum).CoolingLimit = LimitType::FlowRate;
     404              :                 }
     405            0 :             } break;
     406              : 
     407            0 :             default: {
     408            0 :                 ShowSevereInvalidKey(state,
     409              :                                      eoh,
     410            0 :                                      s_ipsc->cAlphaFieldNames(6),
     411            0 :                                      s_ipsc->cAlphaArgs(6),
     412              :                                      "Valid entries are None, ConstantSensibleHeatRatio, Humidistat, or ConstantSupplyHumidityRatio");
     413            0 :                 ErrorsFound = true;
     414            0 :             } break;
     415              :             }
     416              : 
     417           14 :             PurchAir(PurchAirNum).MaxCoolVolFlowRate = s_ipsc->rNumericArgs(7);
     418           14 :             PurchAir(PurchAirNum).MaxCoolTotCap = s_ipsc->rNumericArgs(8);
     419              : 
     420              :             // get optional heating availability schedule
     421           14 :             if (s_ipsc->lAlphaFieldBlanks(8)) {
     422           14 :                 PurchAir(PurchAirNum).heatAvailSched = Sched::GetScheduleAlwaysOn(state);
     423            0 :             } else if ((PurchAir(PurchAirNum).heatAvailSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(8))) == nullptr) {
     424            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8));
     425            0 :                 ErrorsFound = true;
     426              :             }
     427              : 
     428              :             // get optional cooling availability schedule
     429           14 :             if (s_ipsc->lAlphaFieldBlanks(9)) {
     430           14 :                 PurchAir(PurchAirNum).coolAvailSched = Sched::GetScheduleAlwaysOn(state);
     431            0 :             } else if ((PurchAir(PurchAirNum).coolAvailSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(9))) == nullptr) {
     432            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(9), s_ipsc->cAlphaArgs(9));
     433            0 :                 ErrorsFound = true;
     434              :             }
     435              : 
     436              :             // get Dehumidification control type
     437           14 :             PurchAir(PurchAirNum).DehumidCtrlType = static_cast<HumControl>(getEnumValue(humControlNamesUC, s_ipsc->cAlphaArgs(10)));
     438           14 :             if (PurchAir(PurchAirNum).DehumidCtrlType == HumControl::Invalid) {
     439            0 :                 ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
     440            0 :                 ErrorsFound = true;
     441              :             }
     442              : 
     443           14 :             PurchAir(PurchAirNum).CoolSHR = s_ipsc->rNumericArgs(9);
     444              : 
     445              :             // get Humidification control type
     446           14 :             PurchAir(PurchAirNum).HumidCtrlType = static_cast<HumControl>(getEnumValue(humControlNamesUC, s_ipsc->cAlphaArgs(11)));
     447           28 :             if (PurchAir(PurchAirNum).HumidCtrlType == HumControl::Invalid ||
     448           14 :                 PurchAir(PurchAirNum).HumidCtrlType == HumControl::ConstantSensibleHeatRatio) {
     449            0 :                 ShowSevereInvalidKey(state,
     450              :                                      eoh,
     451            0 :                                      s_ipsc->cAlphaFieldNames(11),
     452            0 :                                      s_ipsc->cAlphaArgs(11),
     453              :                                      "Valid entries are None, Humidistat, or ConstantSupplyHumidityRatio");
     454            0 :                 ErrorsFound = true;
     455              :             }
     456              : 
     457              :             // get Design specification outdoor air object
     458           14 :             if (!s_ipsc->lAlphaFieldBlanks(12)) {
     459            1 :                 PurchAir(PurchAirNum).OARequirementsPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(12), state.dataSize->OARequirements);
     460            1 :                 if (PurchAir(PurchAirNum).OARequirementsPtr == 0) {
     461            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(12), s_ipsc->cAlphaArgs(12));
     462            0 :                     ErrorsFound = true;
     463              :                 } else {
     464            1 :                     PurchAir(PurchAirNum).OutdoorAir = true;
     465              :                 }
     466              :             }
     467              : 
     468              :             // If outdoor air specified, then get Outdoor air inlet node and other outdoor air inputs
     469           14 :             if (PurchAir(PurchAirNum).OutdoorAir) {
     470            1 :                 if (s_ipsc->lAlphaFieldBlanks(13)) {
     471              :                     // If there is outdoor air and outdoor air inlet node is blank, then create one
     472            1 :                     if (len(s_ipsc->cAlphaArgs(1)) < Constant::MaxNameLength - 23) { // protect against long name leading to > 100 chars
     473            1 :                         s_ipsc->cAlphaArgs(13) = s_ipsc->cAlphaArgs(1) + " OUTDOOR AIR INLET NODE";
     474              :                     } else {
     475            0 :                         s_ipsc->cAlphaArgs(13) = s_ipsc->cAlphaArgs(1).substr(0, 75) + " OUTDOOR AIR INLET NODE";
     476              :                     }
     477            1 :                     if (state.dataGlobal->DisplayExtraWarnings) {
     478            0 :                         ShowWarningError(state, format("{}{}=\"{} blank field", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
     479            0 :                         ShowContinueError(state,
     480            0 :                                           format("{} is blank, but there is outdoor air requested for this system.", s_ipsc->cAlphaFieldNames(13)));
     481            0 :                         ShowContinueError(state, format("Creating node name ={}", s_ipsc->cAlphaArgs(13)));
     482              :                     }
     483              :                 }
     484              :                 // Register OA node
     485            2 :                 PurchAir(PurchAirNum).OutdoorAirNodeNum = GetOnlySingleNode(state,
     486            1 :                                                                             s_ipsc->cAlphaArgs(13),
     487              :                                                                             ErrorsFound,
     488              :                                                                             DataLoopNode::ConnectionObjectType::ZoneHVACIdealLoadsAirSystem,
     489            1 :                                                                             s_ipsc->cAlphaArgs(1),
     490              :                                                                             DataLoopNode::NodeFluidType::Air,
     491              :                                                                             DataLoopNode::ConnectionType::Outlet,
     492              :                                                                             NodeInputManager::CompFluidStream::Primary,
     493              :                                                                             ObjectIsNotParent);
     494              :                 // Check if OA node is initialized in OutdoorAir:Node or OutdoorAir:Nodelist
     495              :                 bool IsOANodeListed; // Flag for OA node name listed in OutdoorAir:Node or Nodelist
     496            1 :                 CheckAndAddAirNodeNumber(state, PurchAir(PurchAirNum).OutdoorAirNodeNum, IsOANodeListed);
     497            1 :                 if ((!IsOANodeListed) && state.dataGlobal->DisplayExtraWarnings) {
     498            0 :                     ShowWarningError(state, format("{}{}=\"{} missing data", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
     499            0 :                     ShowContinueError(state,
     500            0 :                                       format("{} does not appear in an OutdoorAir:NodeList or as an OutdoorAir:Node.", s_ipsc->cAlphaArgs(13)));
     501            0 :                     ShowContinueError(state, format("Adding OutdoorAir:Node={}", s_ipsc->cAlphaArgs(13)));
     502              :                 }
     503            1 :                 UniqueNodeError = false;
     504            1 :                 CheckUniqueNodeNames(state, s_ipsc->cAlphaFieldNames(13), UniqueNodeError, s_ipsc->cAlphaArgs(13), s_ipsc->cAlphaArgs(1));
     505            1 :                 if (UniqueNodeError) ErrorsFound = true;
     506              : 
     507              :                 // get Demand controlled ventilation type
     508            1 :                 PurchAir(PurchAirNum).DCVType = static_cast<DCV>(getEnumValue(dcvNamesUC, s_ipsc->cAlphaArgs(14)));
     509            1 :                 if (PurchAir(PurchAirNum).DCVType == DCV::Invalid) {
     510            0 :                     ShowSevereInvalidKey(state,
     511              :                                          eoh,
     512            0 :                                          s_ipsc->cAlphaFieldNames(14),
     513            0 :                                          s_ipsc->cAlphaArgs(14),
     514              :                                          "Valid entries are None, OccupancySchedule, or CO2Setpoint");
     515            0 :                     ErrorsFound = true;
     516            1 :                 } else if (PurchAir(PurchAirNum).DCVType == DCV::CO2SetPoint && !state.dataContaminantBalance->Contaminant.CO2Simulation) {
     517            0 :                     PurchAir(PurchAirNum).DCVType = DCV::None;
     518            0 :                     ShowWarningError(state, format("{}{}=\"{} invalid data", RoutineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
     519            0 :                     ShowContinueError(state, format("{}={} but CO2 simulation is not active.", s_ipsc->cAlphaFieldNames(14), s_ipsc->cAlphaArgs(14)));
     520            0 :                     ShowContinueError(state, format("Resetting {} to NoDCV", s_ipsc->cAlphaFieldNames(14)));
     521            0 :                     ShowContinueError(state,
     522              :                                       "To activate CO2 simulation, use ZoneAirContaminantBalance object and specify \"Carbon Dioxide "
     523              :                                       "Concentration\"=\"Yes\".");
     524              :                 }
     525              : 
     526              :                 // get Outdoor air economizer type
     527            1 :                 PurchAir(PurchAirNum).EconomizerType = static_cast<Econ>(getEnumValue(econNamesUC, s_ipsc->cAlphaArgs(15)));
     528            1 :                 if (PurchAir(PurchAirNum).EconomizerType == Econ::Invalid) {
     529            0 :                     ShowSevereInvalidKey(state,
     530              :                                          eoh,
     531            0 :                                          s_ipsc->cAlphaFieldNames(15),
     532            0 :                                          s_ipsc->cAlphaArgs(15),
     533              :                                          "Valid entries are NoEconomizer, DifferentialDryBulb, or DifferentialEnthalpy");
     534            0 :                     ErrorsFound = true;
     535              :                 }
     536              : 
     537              :                 // get Outdoor air heat recovery type and effectiveness
     538            1 :                 PurchAir(PurchAirNum).HtRecType = static_cast<HeatRecovery>(getEnumValue(heatRecoveryNamesUC, s_ipsc->cAlphaArgs(16)));
     539            1 :                 if (PurchAir(PurchAirNum).HtRecType == HeatRecovery::Invalid) {
     540            0 :                     ShowSevereInvalidKey(
     541            0 :                         state, eoh, s_ipsc->cAlphaFieldNames(16), s_ipsc->cAlphaArgs(16), "Valid entries are None, Sensible, or Enthalpy");
     542            0 :                     ErrorsFound = true;
     543              :                 }
     544              : 
     545              :             } else { // No outdoorair
     546           13 :                 PurchAir(PurchAirNum).DCVType = DCV::None;
     547           13 :                 PurchAir(PurchAirNum).EconomizerType = Econ::NoEconomizer;
     548           13 :                 PurchAir(PurchAirNum).HtRecType = HeatRecovery::None;
     549              :             }
     550              : 
     551           14 :             PurchAir(PurchAirNum).HtRecSenEff = s_ipsc->rNumericArgs(10);
     552           14 :             PurchAir(PurchAirNum).HtRecLatEff = s_ipsc->rNumericArgs(11);
     553              : 
     554           28 :             for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     555           14 :                 if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
     556           26 :                 for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++NodeNum) {
     557           13 :                     if (PurchAir(PurchAirNum).ZoneSupplyAirNodeNum == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(NodeNum)) {
     558           13 :                         PurchAir(PurchAirNum).ZonePtr = CtrlZone;
     559              :                     }
     560              :                 }
     561              :             }
     562              : 
     563           14 :             PurchAir(PurchAirNum).HVACSizingIndex = 0;
     564           14 :             if (!s_ipsc->lAlphaFieldBlanks(17)) {
     565            0 :                 PurchAir(PurchAirNum).HVACSizingIndex = Util::FindItemInList(s_ipsc->cAlphaArgs(17), state.dataSize->ZoneHVACSizing);
     566            0 :                 if (PurchAir(PurchAirNum).HVACSizingIndex == 0) {
     567            0 :                     ShowSevereError(state, format("{} = {} not found.", s_ipsc->cAlphaFieldNames(17), s_ipsc->cAlphaArgs(17)));
     568            0 :                     ShowContinueError(state, format("Occurs in {} = {}", s_ipsc->cCurrentModuleObject, PurchAir(PurchAirNum).Name));
     569            0 :                     ErrorsFound = true;
     570              :                 }
     571              :             }
     572              : 
     573              :             // initialize the calculated and report values
     574           14 :             PurchAir(PurchAirNum).MaxHeatMassFlowRate = 0.0;
     575           14 :             PurchAir(PurchAirNum).MaxCoolMassFlowRate = 0.0;
     576           14 :             PurchAir(PurchAirNum).SenHeatEnergy = 0.0;
     577           14 :             PurchAir(PurchAirNum).LatHeatEnergy = 0.0;
     578           14 :             PurchAir(PurchAirNum).TotHeatEnergy = 0.0;
     579           14 :             PurchAir(PurchAirNum).SenCoolEnergy = 0.0;
     580           14 :             PurchAir(PurchAirNum).LatCoolEnergy = 0.0;
     581           14 :             PurchAir(PurchAirNum).TotCoolEnergy = 0.0;
     582           14 :             PurchAir(PurchAirNum).ZoneSenHeatEnergy = 0.0;
     583           14 :             PurchAir(PurchAirNum).ZoneLatHeatEnergy = 0.0;
     584           14 :             PurchAir(PurchAirNum).ZoneTotHeatEnergy = 0.0;
     585           14 :             PurchAir(PurchAirNum).ZoneSenCoolEnergy = 0.0;
     586           14 :             PurchAir(PurchAirNum).ZoneLatCoolEnergy = 0.0;
     587           14 :             PurchAir(PurchAirNum).ZoneTotCoolEnergy = 0.0;
     588           14 :             PurchAir(PurchAirNum).OASenHeatEnergy = 0.0;
     589           14 :             PurchAir(PurchAirNum).OALatHeatEnergy = 0.0;
     590           14 :             PurchAir(PurchAirNum).OATotHeatEnergy = 0.0;
     591           14 :             PurchAir(PurchAirNum).OASenCoolEnergy = 0.0;
     592           14 :             PurchAir(PurchAirNum).OALatCoolEnergy = 0.0;
     593           14 :             PurchAir(PurchAirNum).OATotCoolEnergy = 0.0;
     594           14 :             PurchAir(PurchAirNum).HtRecSenHeatEnergy = 0.0;
     595           14 :             PurchAir(PurchAirNum).HtRecLatHeatEnergy = 0.0;
     596           14 :             PurchAir(PurchAirNum).HtRecTotHeatEnergy = 0.0;
     597           14 :             PurchAir(PurchAirNum).HtRecSenCoolEnergy = 0.0;
     598           14 :             PurchAir(PurchAirNum).HtRecLatCoolEnergy = 0.0;
     599           14 :             PurchAir(PurchAirNum).HtRecTotCoolEnergy = 0.0;
     600           14 :             PurchAir(PurchAirNum).SenHeatRate = 0.0;
     601           14 :             PurchAir(PurchAirNum).LatHeatRate = 0.0;
     602           14 :             PurchAir(PurchAirNum).TotHeatRate = 0.0;
     603           14 :             PurchAir(PurchAirNum).SenCoolRate = 0.0;
     604           14 :             PurchAir(PurchAirNum).LatCoolRate = 0.0;
     605           14 :             PurchAir(PurchAirNum).TotCoolRate = 0.0;
     606           14 :             PurchAir(PurchAirNum).ZoneSenHeatRate = 0.0;
     607           14 :             PurchAir(PurchAirNum).ZoneLatHeatRate = 0.0;
     608           14 :             PurchAir(PurchAirNum).ZoneTotHeatRate = 0.0;
     609           14 :             PurchAir(PurchAirNum).ZoneSenCoolRate = 0.0;
     610           14 :             PurchAir(PurchAirNum).ZoneLatCoolRate = 0.0;
     611           14 :             PurchAir(PurchAirNum).ZoneTotCoolRate = 0.0;
     612           14 :             PurchAir(PurchAirNum).OASenHeatRate = 0.0;
     613           14 :             PurchAir(PurchAirNum).OALatHeatRate = 0.0;
     614           14 :             PurchAir(PurchAirNum).OATotHeatRate = 0.0;
     615           14 :             PurchAir(PurchAirNum).OASenCoolRate = 0.0;
     616           14 :             PurchAir(PurchAirNum).OALatCoolRate = 0.0;
     617           14 :             PurchAir(PurchAirNum).OATotCoolRate = 0.0;
     618           14 :             PurchAir(PurchAirNum).HtRecSenHeatRate = 0.0;
     619           14 :             PurchAir(PurchAirNum).HtRecLatHeatRate = 0.0;
     620           14 :             PurchAir(PurchAirNum).HtRecTotHeatRate = 0.0;
     621           14 :             PurchAir(PurchAirNum).HtRecSenCoolRate = 0.0;
     622           14 :             PurchAir(PurchAirNum).HtRecLatCoolRate = 0.0;
     623           14 :             PurchAir(PurchAirNum).HtRecTotCoolRate = 0.0;
     624              : 
     625           14 :             PurchAir(PurchAirNum).OutdoorAirMassFlowRate = 0.0;
     626           14 :             PurchAir(PurchAirNum).OutdoorAirVolFlowRateStdRho = 0.0;
     627           14 :             PurchAir(PurchAirNum).SupplyAirMassFlowRate = 0.0;
     628           14 :             PurchAir(PurchAirNum).SupplyAirVolFlowRateStdRho = 0.0;
     629              :         }
     630           14 :         EndUniqueNodeCheck(state, s_ipsc->cCurrentModuleObject);
     631              :     }
     632              : 
     633           31 :     for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
     634              : 
     635              :         // Setup Output variables
     636              :         //    energy variables
     637           28 :         SetupOutputVariable(state,
     638              :                             "Zone Ideal Loads Supply Air Sensible Heating Energy",
     639              :                             Constant::Units::J,
     640           14 :                             PurchAir(PurchAirNum).SenHeatEnergy,
     641              :                             OutputProcessor::TimeStepType::System,
     642              :                             OutputProcessor::StoreType::Sum,
     643           14 :                             PurchAir(PurchAirNum).Name);
     644           28 :         SetupOutputVariable(state,
     645              :                             "Zone Ideal Loads Supply Air Latent Heating Energy",
     646              :                             Constant::Units::J,
     647           14 :                             PurchAir(PurchAirNum).LatHeatEnergy,
     648              :                             OutputProcessor::TimeStepType::System,
     649              :                             OutputProcessor::StoreType::Sum,
     650           14 :                             PurchAir(PurchAirNum).Name);
     651           28 :         SetupOutputVariable(state,
     652              :                             "Zone Ideal Loads Supply Air Total Heating Energy",
     653              :                             Constant::Units::J,
     654           14 :                             PurchAir(PurchAirNum).TotHeatEnergy,
     655              :                             OutputProcessor::TimeStepType::System,
     656              :                             OutputProcessor::StoreType::Sum,
     657           14 :                             PurchAir(PurchAirNum).Name,
     658              :                             Constant::eResource::DistrictHeatingWater,
     659              :                             OutputProcessor::Group::HVAC,
     660              :                             OutputProcessor::EndUseCat::Heating);
     661           28 :         SetupOutputVariable(state,
     662              :                             "Zone Ideal Loads Supply Air Sensible Cooling Energy",
     663              :                             Constant::Units::J,
     664           14 :                             PurchAir(PurchAirNum).SenCoolEnergy,
     665              :                             OutputProcessor::TimeStepType::System,
     666              :                             OutputProcessor::StoreType::Sum,
     667           14 :                             PurchAir(PurchAirNum).Name);
     668           28 :         SetupOutputVariable(state,
     669              :                             "Zone Ideal Loads Supply Air Latent Cooling Energy",
     670              :                             Constant::Units::J,
     671           14 :                             PurchAir(PurchAirNum).LatCoolEnergy,
     672              :                             OutputProcessor::TimeStepType::System,
     673              :                             OutputProcessor::StoreType::Sum,
     674           14 :                             PurchAir(PurchAirNum).Name);
     675           28 :         SetupOutputVariable(state,
     676              :                             "Zone Ideal Loads Supply Air Total Cooling Energy",
     677              :                             Constant::Units::J,
     678           14 :                             PurchAir(PurchAirNum).TotCoolEnergy,
     679              :                             OutputProcessor::TimeStepType::System,
     680              :                             OutputProcessor::StoreType::Sum,
     681           14 :                             PurchAir(PurchAirNum).Name,
     682              :                             Constant::eResource::DistrictCooling,
     683              :                             OutputProcessor::Group::HVAC,
     684              :                             OutputProcessor::EndUseCat::Cooling);
     685           28 :         SetupOutputVariable(state,
     686              :                             "Zone Ideal Loads Zone Sensible Heating Energy",
     687              :                             Constant::Units::J,
     688           14 :                             PurchAir(PurchAirNum).ZoneSenHeatEnergy,
     689              :                             OutputProcessor::TimeStepType::System,
     690              :                             OutputProcessor::StoreType::Sum,
     691           14 :                             PurchAir(PurchAirNum).Name);
     692           28 :         SetupOutputVariable(state,
     693              :                             "Zone Ideal Loads Zone Latent Heating Energy",
     694              :                             Constant::Units::J,
     695           14 :                             PurchAir(PurchAirNum).ZoneLatHeatEnergy,
     696              :                             OutputProcessor::TimeStepType::System,
     697              :                             OutputProcessor::StoreType::Sum,
     698           14 :                             PurchAir(PurchAirNum).Name);
     699           28 :         SetupOutputVariable(state,
     700              :                             "Zone Ideal Loads Zone Total Heating Energy",
     701              :                             Constant::Units::J,
     702           14 :                             PurchAir(PurchAirNum).ZoneTotHeatEnergy,
     703              :                             OutputProcessor::TimeStepType::System,
     704              :                             OutputProcessor::StoreType::Sum,
     705           14 :                             PurchAir(PurchAirNum).Name);
     706           28 :         SetupOutputVariable(state,
     707              :                             "Zone Ideal Loads Zone Sensible Cooling Energy",
     708              :                             Constant::Units::J,
     709           14 :                             PurchAir(PurchAirNum).ZoneSenCoolEnergy,
     710              :                             OutputProcessor::TimeStepType::System,
     711              :                             OutputProcessor::StoreType::Sum,
     712           14 :                             PurchAir(PurchAirNum).Name);
     713           28 :         SetupOutputVariable(state,
     714              :                             "Zone Ideal Loads Zone Latent Cooling Energy",
     715              :                             Constant::Units::J,
     716           14 :                             PurchAir(PurchAirNum).ZoneLatCoolEnergy,
     717              :                             OutputProcessor::TimeStepType::System,
     718              :                             OutputProcessor::StoreType::Sum,
     719           14 :                             PurchAir(PurchAirNum).Name);
     720           28 :         SetupOutputVariable(state,
     721              :                             "Zone Ideal Loads Zone Total Cooling Energy",
     722              :                             Constant::Units::J,
     723           14 :                             PurchAir(PurchAirNum).ZoneTotCoolEnergy,
     724              :                             OutputProcessor::TimeStepType::System,
     725              :                             OutputProcessor::StoreType::Sum,
     726           14 :                             PurchAir(PurchAirNum).Name);
     727           28 :         SetupOutputVariable(state,
     728              :                             "Zone Ideal Loads Outdoor Air Sensible Heating Energy",
     729              :                             Constant::Units::J,
     730           14 :                             PurchAir(PurchAirNum).OASenHeatEnergy,
     731              :                             OutputProcessor::TimeStepType::System,
     732              :                             OutputProcessor::StoreType::Sum,
     733           14 :                             PurchAir(PurchAirNum).Name);
     734           28 :         SetupOutputVariable(state,
     735              :                             "Zone Ideal Loads Outdoor Air Latent Heating Energy",
     736              :                             Constant::Units::J,
     737           14 :                             PurchAir(PurchAirNum).OALatHeatEnergy,
     738              :                             OutputProcessor::TimeStepType::System,
     739              :                             OutputProcessor::StoreType::Sum,
     740           14 :                             PurchAir(PurchAirNum).Name);
     741           28 :         SetupOutputVariable(state,
     742              :                             "Zone Ideal Loads Outdoor Air Total Heating Energy",
     743              :                             Constant::Units::J,
     744           14 :                             PurchAir(PurchAirNum).OATotHeatEnergy,
     745              :                             OutputProcessor::TimeStepType::System,
     746              :                             OutputProcessor::StoreType::Sum,
     747           14 :                             PurchAir(PurchAirNum).Name);
     748           28 :         SetupOutputVariable(state,
     749              :                             "Zone Ideal Loads Outdoor Air Sensible Cooling Energy",
     750              :                             Constant::Units::J,
     751           14 :                             PurchAir(PurchAirNum).OASenCoolEnergy,
     752              :                             OutputProcessor::TimeStepType::System,
     753              :                             OutputProcessor::StoreType::Sum,
     754           14 :                             PurchAir(PurchAirNum).Name);
     755           28 :         SetupOutputVariable(state,
     756              :                             "Zone Ideal Loads Outdoor Air Latent Cooling Energy",
     757              :                             Constant::Units::J,
     758           14 :                             PurchAir(PurchAirNum).OALatCoolEnergy,
     759              :                             OutputProcessor::TimeStepType::System,
     760              :                             OutputProcessor::StoreType::Sum,
     761           14 :                             PurchAir(PurchAirNum).Name);
     762           28 :         SetupOutputVariable(state,
     763              :                             "Zone Ideal Loads Outdoor Air Total Cooling Energy",
     764              :                             Constant::Units::J,
     765           14 :                             PurchAir(PurchAirNum).OATotCoolEnergy,
     766              :                             OutputProcessor::TimeStepType::System,
     767              :                             OutputProcessor::StoreType::Sum,
     768           14 :                             PurchAir(PurchAirNum).Name);
     769           28 :         SetupOutputVariable(state,
     770              :                             "Zone Ideal Loads Heat Recovery Sensible Heating Energy",
     771              :                             Constant::Units::J,
     772           14 :                             PurchAir(PurchAirNum).HtRecSenHeatEnergy,
     773              :                             OutputProcessor::TimeStepType::System,
     774              :                             OutputProcessor::StoreType::Sum,
     775           14 :                             PurchAir(PurchAirNum).Name);
     776           28 :         SetupOutputVariable(state,
     777              :                             "Zone Ideal Loads Heat Recovery Latent Heating Energy",
     778              :                             Constant::Units::J,
     779           14 :                             PurchAir(PurchAirNum).HtRecLatHeatEnergy,
     780              :                             OutputProcessor::TimeStepType::System,
     781              :                             OutputProcessor::StoreType::Sum,
     782           14 :                             PurchAir(PurchAirNum).Name);
     783           28 :         SetupOutputVariable(state,
     784              :                             "Zone Ideal Loads Heat Recovery Total Heating Energy",
     785              :                             Constant::Units::J,
     786           14 :                             PurchAir(PurchAirNum).HtRecTotHeatEnergy,
     787              :                             OutputProcessor::TimeStepType::System,
     788              :                             OutputProcessor::StoreType::Sum,
     789           14 :                             PurchAir(PurchAirNum).Name);
     790           28 :         SetupOutputVariable(state,
     791              :                             "Zone Ideal Loads Heat Recovery Sensible Cooling Energy",
     792              :                             Constant::Units::J,
     793           14 :                             PurchAir(PurchAirNum).HtRecSenCoolEnergy,
     794              :                             OutputProcessor::TimeStepType::System,
     795              :                             OutputProcessor::StoreType::Sum,
     796           14 :                             PurchAir(PurchAirNum).Name);
     797           28 :         SetupOutputVariable(state,
     798              :                             "Zone Ideal Loads Heat Recovery Latent Cooling Energy",
     799              :                             Constant::Units::J,
     800           14 :                             PurchAir(PurchAirNum).HtRecLatCoolEnergy,
     801              :                             OutputProcessor::TimeStepType::System,
     802              :                             OutputProcessor::StoreType::Sum,
     803           14 :                             PurchAir(PurchAirNum).Name);
     804           28 :         SetupOutputVariable(state,
     805              :                             "Zone Ideal Loads Heat Recovery Total Cooling Energy",
     806              :                             Constant::Units::J,
     807           14 :                             PurchAir(PurchAirNum).HtRecTotCoolEnergy,
     808              :                             OutputProcessor::TimeStepType::System,
     809              :                             OutputProcessor::StoreType::Sum,
     810           14 :                             PurchAir(PurchAirNum).Name);
     811              : 
     812              :         //    rate variables
     813           28 :         SetupOutputVariable(state,
     814              :                             "Zone Ideal Loads Supply Air Sensible Heating Rate",
     815              :                             Constant::Units::W,
     816           14 :                             PurchAir(PurchAirNum).SenHeatRate,
     817              :                             OutputProcessor::TimeStepType::System,
     818              :                             OutputProcessor::StoreType::Average,
     819           14 :                             PurchAir(PurchAirNum).Name);
     820           28 :         SetupOutputVariable(state,
     821              :                             "Zone Ideal Loads Supply Air Latent Heating Rate",
     822              :                             Constant::Units::W,
     823           14 :                             PurchAir(PurchAirNum).LatHeatRate,
     824              :                             OutputProcessor::TimeStepType::System,
     825              :                             OutputProcessor::StoreType::Average,
     826           14 :                             PurchAir(PurchAirNum).Name);
     827           28 :         SetupOutputVariable(state,
     828              :                             "Zone Ideal Loads Supply Air Total Heating Rate",
     829              :                             Constant::Units::W,
     830           14 :                             PurchAir(PurchAirNum).TotHeatRate,
     831              :                             OutputProcessor::TimeStepType::System,
     832              :                             OutputProcessor::StoreType::Average,
     833           14 :                             PurchAir(PurchAirNum).Name);
     834           28 :         SetupOutputVariable(state,
     835              :                             "Zone Ideal Loads Supply Air Sensible Cooling Rate",
     836              :                             Constant::Units::W,
     837           14 :                             PurchAir(PurchAirNum).SenCoolRate,
     838              :                             OutputProcessor::TimeStepType::System,
     839              :                             OutputProcessor::StoreType::Average,
     840           14 :                             PurchAir(PurchAirNum).Name);
     841           28 :         SetupOutputVariable(state,
     842              :                             "Zone Ideal Loads Supply Air Latent Cooling Rate",
     843              :                             Constant::Units::W,
     844           14 :                             PurchAir(PurchAirNum).LatCoolRate,
     845              :                             OutputProcessor::TimeStepType::System,
     846              :                             OutputProcessor::StoreType::Average,
     847           14 :                             PurchAir(PurchAirNum).Name);
     848           28 :         SetupOutputVariable(state,
     849              :                             "Zone Ideal Loads Supply Air Total Cooling Rate",
     850              :                             Constant::Units::W,
     851           14 :                             PurchAir(PurchAirNum).TotCoolRate,
     852              :                             OutputProcessor::TimeStepType::System,
     853              :                             OutputProcessor::StoreType::Average,
     854           14 :                             PurchAir(PurchAirNum).Name);
     855           28 :         SetupOutputVariable(state,
     856              :                             "Zone Ideal Loads Zone Sensible Heating Rate",
     857              :                             Constant::Units::W,
     858           14 :                             PurchAir(PurchAirNum).ZoneSenHeatRate,
     859              :                             OutputProcessor::TimeStepType::System,
     860              :                             OutputProcessor::StoreType::Average,
     861           14 :                             PurchAir(PurchAirNum).Name);
     862           28 :         SetupOutputVariable(state,
     863              :                             "Zone Ideal Loads Zone Latent Heating Rate",
     864              :                             Constant::Units::W,
     865           14 :                             PurchAir(PurchAirNum).ZoneLatHeatRate,
     866              :                             OutputProcessor::TimeStepType::System,
     867              :                             OutputProcessor::StoreType::Average,
     868           14 :                             PurchAir(PurchAirNum).Name);
     869           28 :         SetupOutputVariable(state,
     870              :                             "Zone Ideal Loads Zone Total Heating Rate",
     871              :                             Constant::Units::W,
     872           14 :                             PurchAir(PurchAirNum).ZoneTotHeatRate,
     873              :                             OutputProcessor::TimeStepType::System,
     874              :                             OutputProcessor::StoreType::Average,
     875           14 :                             PurchAir(PurchAirNum).Name);
     876           28 :         SetupOutputVariable(state,
     877              :                             "Zone Ideal Loads Zone Sensible Cooling Rate",
     878              :                             Constant::Units::W,
     879           14 :                             PurchAir(PurchAirNum).ZoneSenCoolRate,
     880              :                             OutputProcessor::TimeStepType::System,
     881              :                             OutputProcessor::StoreType::Average,
     882           14 :                             PurchAir(PurchAirNum).Name);
     883           28 :         SetupOutputVariable(state,
     884              :                             "Zone Ideal Loads Zone Latent Cooling Rate",
     885              :                             Constant::Units::W,
     886           14 :                             PurchAir(PurchAirNum).ZoneLatCoolRate,
     887              :                             OutputProcessor::TimeStepType::System,
     888              :                             OutputProcessor::StoreType::Average,
     889           14 :                             PurchAir(PurchAirNum).Name);
     890           28 :         SetupOutputVariable(state,
     891              :                             "Zone Ideal Loads Zone Total Cooling Rate",
     892              :                             Constant::Units::W,
     893           14 :                             PurchAir(PurchAirNum).ZoneTotCoolRate,
     894              :                             OutputProcessor::TimeStepType::System,
     895              :                             OutputProcessor::StoreType::Average,
     896           14 :                             PurchAir(PurchAirNum).Name);
     897           28 :         SetupOutputVariable(state,
     898              :                             "Zone Ideal Loads Outdoor Air Sensible Heating Rate",
     899              :                             Constant::Units::W,
     900           14 :                             PurchAir(PurchAirNum).OASenHeatRate,
     901              :                             OutputProcessor::TimeStepType::System,
     902              :                             OutputProcessor::StoreType::Average,
     903           14 :                             PurchAir(PurchAirNum).Name);
     904           28 :         SetupOutputVariable(state,
     905              :                             "Zone Ideal Loads Outdoor Air Latent Heating Rate",
     906              :                             Constant::Units::W,
     907           14 :                             PurchAir(PurchAirNum).OALatHeatRate,
     908              :                             OutputProcessor::TimeStepType::System,
     909              :                             OutputProcessor::StoreType::Average,
     910           14 :                             PurchAir(PurchAirNum).Name);
     911           28 :         SetupOutputVariable(state,
     912              :                             "Zone Ideal Loads Outdoor Air Total Heating Rate",
     913              :                             Constant::Units::W,
     914           14 :                             PurchAir(PurchAirNum).OATotHeatRate,
     915              :                             OutputProcessor::TimeStepType::System,
     916              :                             OutputProcessor::StoreType::Average,
     917           14 :                             PurchAir(PurchAirNum).Name);
     918           28 :         SetupOutputVariable(state,
     919              :                             "Zone Ideal Loads Outdoor Air Sensible Cooling Rate",
     920              :                             Constant::Units::W,
     921           14 :                             PurchAir(PurchAirNum).OASenCoolRate,
     922              :                             OutputProcessor::TimeStepType::System,
     923              :                             OutputProcessor::StoreType::Average,
     924           14 :                             PurchAir(PurchAirNum).Name);
     925           28 :         SetupOutputVariable(state,
     926              :                             "Zone Ideal Loads Outdoor Air Latent Cooling Rate",
     927              :                             Constant::Units::W,
     928           14 :                             PurchAir(PurchAirNum).OALatCoolRate,
     929              :                             OutputProcessor::TimeStepType::System,
     930              :                             OutputProcessor::StoreType::Average,
     931           14 :                             PurchAir(PurchAirNum).Name);
     932           28 :         SetupOutputVariable(state,
     933              :                             "Zone Ideal Loads Outdoor Air Total Cooling Rate",
     934              :                             Constant::Units::W,
     935           14 :                             PurchAir(PurchAirNum).OATotCoolRate,
     936              :                             OutputProcessor::TimeStepType::System,
     937              :                             OutputProcessor::StoreType::Average,
     938           14 :                             PurchAir(PurchAirNum).Name);
     939           28 :         SetupOutputVariable(state,
     940              :                             "Zone Ideal Loads Heat Recovery Sensible Heating Rate",
     941              :                             Constant::Units::W,
     942           14 :                             PurchAir(PurchAirNum).HtRecSenHeatRate,
     943              :                             OutputProcessor::TimeStepType::System,
     944              :                             OutputProcessor::StoreType::Average,
     945           14 :                             PurchAir(PurchAirNum).Name);
     946           28 :         SetupOutputVariable(state,
     947              :                             "Zone Ideal Loads Heat Recovery Latent Heating Rate",
     948              :                             Constant::Units::W,
     949           14 :                             PurchAir(PurchAirNum).HtRecLatHeatRate,
     950              :                             OutputProcessor::TimeStepType::System,
     951              :                             OutputProcessor::StoreType::Average,
     952           14 :                             PurchAir(PurchAirNum).Name);
     953           28 :         SetupOutputVariable(state,
     954              :                             "Zone Ideal Loads Heat Recovery Total Heating Rate",
     955              :                             Constant::Units::W,
     956           14 :                             PurchAir(PurchAirNum).HtRecTotHeatRate,
     957              :                             OutputProcessor::TimeStepType::System,
     958              :                             OutputProcessor::StoreType::Average,
     959           14 :                             PurchAir(PurchAirNum).Name);
     960           28 :         SetupOutputVariable(state,
     961              :                             "Zone Ideal Loads Heat Recovery Sensible Cooling Rate",
     962              :                             Constant::Units::W,
     963           14 :                             PurchAir(PurchAirNum).HtRecSenCoolRate,
     964              :                             OutputProcessor::TimeStepType::System,
     965              :                             OutputProcessor::StoreType::Average,
     966           14 :                             PurchAir(PurchAirNum).Name);
     967           28 :         SetupOutputVariable(state,
     968              :                             "Zone Ideal Loads Heat Recovery Latent Cooling Rate",
     969              :                             Constant::Units::W,
     970           14 :                             PurchAir(PurchAirNum).HtRecLatCoolRate,
     971              :                             OutputProcessor::TimeStepType::System,
     972              :                             OutputProcessor::StoreType::Average,
     973           14 :                             PurchAir(PurchAirNum).Name);
     974           28 :         SetupOutputVariable(state,
     975              :                             "Zone Ideal Loads Heat Recovery Total Cooling Rate",
     976              :                             Constant::Units::W,
     977           14 :                             PurchAir(PurchAirNum).HtRecTotCoolRate,
     978              :                             OutputProcessor::TimeStepType::System,
     979              :                             OutputProcessor::StoreType::Average,
     980           14 :                             PurchAir(PurchAirNum).Name);
     981              : 
     982           28 :         SetupOutputVariable(state,
     983              :                             "Zone Ideal Loads Economizer Active Time",
     984              :                             Constant::Units::hr,
     985           14 :                             PurchAir(PurchAirNum).TimeEconoActive,
     986              :                             OutputProcessor::TimeStepType::System,
     987              :                             OutputProcessor::StoreType::Sum,
     988           14 :                             PurchAir(PurchAirNum).Name);
     989           28 :         SetupOutputVariable(state,
     990              :                             "Zone Ideal Loads Heat Recovery Active Time",
     991              :                             Constant::Units::hr,
     992           14 :                             PurchAir(PurchAirNum).TimeHtRecActive,
     993              :                             OutputProcessor::TimeStepType::System,
     994              :                             OutputProcessor::StoreType::Sum,
     995           14 :                             PurchAir(PurchAirNum).Name);
     996              : 
     997           14 :         SetupOutputVariable(state,
     998              :                             "Zone Ideal Loads Hybrid Ventilation Available Status",
     999              :                             Constant::Units::None,
    1000           14 :                             (int &)PurchAir(PurchAirNum).availStatus,
    1001              :                             OutputProcessor::TimeStepType::System,
    1002              :                             OutputProcessor::StoreType::Average,
    1003           14 :                             PurchAir(PurchAirNum).Name);
    1004              : 
    1005              :         // air flows
    1006           28 :         SetupOutputVariable(state,
    1007              :                             "Zone Ideal Loads Outdoor Air Mass Flow Rate",
    1008              :                             Constant::Units::kg_s,
    1009           14 :                             PurchAir(PurchAirNum).OutdoorAirMassFlowRate,
    1010              :                             OutputProcessor::TimeStepType::System,
    1011              :                             OutputProcessor::StoreType::Average,
    1012           14 :                             PurchAir(PurchAirNum).Name);
    1013           28 :         SetupOutputVariable(state,
    1014              :                             "Zone Ideal Loads Outdoor Air Standard Density Volume Flow Rate",
    1015              :                             Constant::Units::m3_s,
    1016           14 :                             PurchAir(PurchAirNum).OutdoorAirVolFlowRateStdRho,
    1017              :                             OutputProcessor::TimeStepType::System,
    1018              :                             OutputProcessor::StoreType::Average,
    1019           14 :                             PurchAir(PurchAirNum).Name);
    1020           28 :         SetupOutputVariable(state,
    1021              :                             "Zone Ideal Loads Supply Air Mass Flow Rate",
    1022              :                             Constant::Units::kg_s,
    1023           14 :                             PurchAir(PurchAirNum).SupplyAirMassFlowRate,
    1024              :                             OutputProcessor::TimeStepType::System,
    1025              :                             OutputProcessor::StoreType::Average,
    1026           14 :                             PurchAir(PurchAirNum).Name);
    1027           28 :         SetupOutputVariable(state,
    1028              :                             "Zone Ideal Loads Supply Air Standard Density Volume Flow Rate",
    1029              :                             Constant::Units::m3_s,
    1030           14 :                             PurchAir(PurchAirNum).SupplyAirVolFlowRateStdRho,
    1031              :                             OutputProcessor::TimeStepType::System,
    1032              :                             OutputProcessor::StoreType::Average,
    1033           14 :                             PurchAir(PurchAirNum).Name);
    1034              : 
    1035              :         // Supply Air temperature
    1036           28 :         SetupOutputVariable(state,
    1037              :                             "Zone Ideal Loads Supply Air Temperature",
    1038              :                             Constant::Units::C,
    1039           14 :                             PurchAir(PurchAirNum).SupplyTemp,
    1040              :                             OutputProcessor::TimeStepType::System,
    1041              :                             OutputProcessor::StoreType::Average,
    1042           14 :                             PurchAir(PurchAirNum).Name);
    1043              :         // Supply Air Humidity Ratio
    1044           28 :         SetupOutputVariable(state,
    1045              :                             "Zone Ideal Loads Supply Air Humidity Ratio",
    1046              :                             Constant::Units::kgWater_kgDryAir,
    1047           14 :                             PurchAir(PurchAirNum).SupplyHumRat,
    1048              :                             OutputProcessor::TimeStepType::System,
    1049              :                             OutputProcessor::StoreType::Average,
    1050           14 :                             PurchAir(PurchAirNum).Name);
    1051              : 
    1052              :         // Mixed Air temperature
    1053           28 :         SetupOutputVariable(state,
    1054              :                             "Zone Ideal Loads Mixed Air Temperature",
    1055              :                             Constant::Units::C,
    1056           14 :                             PurchAir(PurchAirNum).MixedAirTemp,
    1057              :                             OutputProcessor::TimeStepType::System,
    1058              :                             OutputProcessor::StoreType::Average,
    1059           14 :                             PurchAir(PurchAirNum).Name);
    1060              :         // Mixed Air Humidity Ratio
    1061           28 :         SetupOutputVariable(state,
    1062              :                             "Zone Ideal Loads Mixed Air Humidity Ratio",
    1063              :                             Constant::Units::kgWater_kgDryAir,
    1064           14 :                             PurchAir(PurchAirNum).MixedAirHumRat,
    1065              :                             OutputProcessor::TimeStepType::System,
    1066              :                             OutputProcessor::StoreType::Average,
    1067           14 :                             PurchAir(PurchAirNum).Name);
    1068              : 
    1069           14 :         if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
    1070            8 :             SetupEMSActuator(state,
    1071              :                              "Ideal Loads Air System",
    1072            4 :                              PurchAir(PurchAirNum).Name,
    1073              :                              "Air Mass Flow Rate",
    1074              :                              "[kg/s]",
    1075            4 :                              PurchAir(PurchAirNum).EMSOverrideMdotOn,
    1076            4 :                              PurchAir(PurchAirNum).EMSValueMassFlowRate);
    1077            8 :             SetupEMSActuator(state,
    1078              :                              "Ideal Loads Air System",
    1079            4 :                              PurchAir(PurchAirNum).Name,
    1080              :                              "Outdoor Air Mass Flow Rate",
    1081              :                              "[kg/s]",
    1082            4 :                              PurchAir(PurchAirNum).EMSOverrideOAMdotOn,
    1083            4 :                              PurchAir(PurchAirNum).EMSValueOAMassFlowRate);
    1084            8 :             SetupEMSActuator(state,
    1085              :                              "Ideal Loads Air System",
    1086            4 :                              PurchAir(PurchAirNum).Name,
    1087              :                              "Air Temperature",
    1088              :                              "[C]",
    1089            4 :                              PurchAir(PurchAirNum).EMSOverrideSupplyTempOn,
    1090            4 :                              PurchAir(PurchAirNum).EMSValueSupplyTemp);
    1091            8 :             SetupEMSActuator(state,
    1092              :                              "Ideal Loads Air System",
    1093            4 :                              PurchAir(PurchAirNum).Name,
    1094              :                              "Air Humidity Ratio",
    1095              :                              "[kgWater/kgDryAir]",
    1096            4 :                              PurchAir(PurchAirNum).EMSOverrideSupplyHumRatOn,
    1097            4 :                              PurchAir(PurchAirNum).EMSValueSupplyHumRat);
    1098              :         }
    1099              :     }
    1100              : 
    1101           17 :     if (ErrorsFound) {
    1102            0 :         ShowFatalError(state, format("{}Errors found in input. Preceding conditions cause termination.", RoutineName));
    1103              :     }
    1104           17 : }
    1105              : 
    1106          766 : void InitPurchasedAir(EnergyPlusData &state, int const PurchAirNum, int const ControlledZoneNum)
    1107              : {
    1108              : 
    1109              :     // SUBROUTINE INFORMATION:
    1110              :     //       AUTHOR         Russ Taylor
    1111              :     //       DATE WRITTEN   Nov 1997
    1112              : 
    1113              :     // PURPOSE OF THIS SUBROUTINE:
    1114              :     // Initialize the PurchAir data structure.
    1115              : 
    1116              :     // Using/Aliasing
    1117              :     using DataZoneEquipment::CheckZoneEquipmentList;
    1118              :     using General::FindNumberInList;
    1119              :     using ZonePlenum::GetReturnPlenumIndex;
    1120              :     using ZonePlenum::GetReturnPlenumName;
    1121              : 
    1122              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1123              :     bool UnitOn; // simple checks for error
    1124              : 
    1125              :     // Do the Begin Simulation initializations
    1126          766 :     if (state.dataPurchasedAirMgr->InitPurchasedAirMyOneTimeFlag) {
    1127           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag.allocate(state.dataPurchasedAirMgr->NumPurchAir);
    1128           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag.allocate(state.dataPurchasedAirMgr->NumPurchAir);
    1129           13 :         state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone.allocate(state.dataPurchasedAirMgr->NumPurchAir);
    1130           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag = true;
    1131           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag = true;
    1132           13 :         state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone = false;
    1133           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMyOneTimeFlag = false;
    1134              :     }
    1135              : 
    1136              :     // need to check all units to see if they are on Zone Equipment List or issue warning
    1137          766 :     if (!state.dataPurchasedAirMgr->InitPurchasedAirZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
    1138            9 :         state.dataPurchasedAirMgr->InitPurchasedAirZoneEquipmentListChecked = true;
    1139           18 :         for (int Loop = 1; Loop <= state.dataPurchasedAirMgr->NumPurchAir; ++Loop) {
    1140            9 :             auto &PurchAirLoop = state.dataPurchasedAirMgr->PurchAir(Loop);
    1141              : 
    1142              :             // link with return plenum if used (i.e., PlenumExhaustAirNodeNum will be non-zero)
    1143            9 :             if (PurchAirLoop.PlenumExhaustAirNodeNum > 0) {
    1144            1 :                 PurchAirLoop.ReturnPlenumIndex = GetReturnPlenumIndex(state, PurchAirLoop.PlenumExhaustAirNodeNum);
    1145            1 :                 if (PurchAirLoop.ReturnPlenumIndex > 0) {
    1146            1 :                     GetReturnPlenumName(state, PurchAirLoop.ReturnPlenumIndex, PurchAirLoop.ReturnPlenumName);
    1147            1 :                     InitializePlenumArrays(state, Loop);
    1148              :                 } else {
    1149            0 :                     ShowSevereError(state,
    1150            0 :                                     format("InitPurchasedAir: {} = {} cannot find ZoneHVAC:ReturnPlenum.  It will not be simulated.",
    1151            0 :                                            PurchAirLoop.cObjectName,
    1152            0 :                                            PurchAirLoop.Name));
    1153              :                 }
    1154              :             }
    1155              : 
    1156            9 :             if (CheckZoneEquipmentList(state, PurchAirLoop.cObjectName, PurchAirLoop.Name)) continue;
    1157            0 :             ShowSevereError(state,
    1158            0 :                             format("InitPurchasedAir: {} = {} is not on any ZoneHVAC:EquipmentList.  It will not be simulated.",
    1159            0 :                                    PurchAirLoop.cObjectName,
    1160            0 :                                    PurchAirLoop.Name));
    1161              :         }
    1162              :     }
    1163              : 
    1164          766 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    1165              :     // one time inits for each unit - links PurchAirNum with static input data from ControlledZoneNum and ActualZoneNum
    1166          766 :     if (!state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone(PurchAirNum)) {
    1167           13 :         state.dataPurchasedAirMgr->InitPurchasedAirOneTimeUnitInitsDone(PurchAirNum) = true;
    1168              : 
    1169              :         // Is the supply node really a zone inlet node?
    1170              :         // this check has to be done here because of SimPurchasedAir passing in ControlledZoneNum
    1171           13 :         int SupplyNodeNum = PurchAir.ZoneSupplyAirNodeNum;
    1172           13 :         if (SupplyNodeNum > 0) {
    1173           13 :             int NodeIndex = FindNumberInList(SupplyNodeNum,
    1174           13 :                                              state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).InletNode,
    1175           13 :                                              state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumInletNodes);
    1176           13 :             if (NodeIndex == 0) {
    1177            0 :                 ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1178            0 :                 ShowContinueError(state,
    1179            0 :                                   format("Zone Supply Air Node Name={} is not a zone inlet node.", state.dataLoopNodes->NodeID(SupplyNodeNum)));
    1180            0 :                 ShowContinueError(
    1181              :                     state,
    1182            0 :                     format("Check ZoneHVAC:EquipmentConnections for zone={}", state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneName));
    1183            0 :                 ShowFatalError(state, "Preceding condition causes termination.");
    1184              :             }
    1185              :         }
    1186              : 
    1187              :         // Set recirculation node number
    1188              :         // If exhaust node is specified, then recirculation is exhaust node, otherwise use zone return node
    1189              :         // this check has to be done here because of SimPurchasedAir passing in ControlledZoneNum
    1190           13 :         bool UseReturnNode = false;
    1191           13 :         if (PurchAir.ZoneExhaustAirNodeNum > 0) {
    1192            8 :             int ExhaustNodeNum = PurchAir.ZoneExhaustAirNodeNum;
    1193            8 :             int NodeIndex = FindNumberInList(ExhaustNodeNum,
    1194            8 :                                              state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ExhaustNode,
    1195            8 :                                              state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumExhaustNodes);
    1196            8 :             if (NodeIndex == 0) {
    1197            0 :                 ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1198            0 :                 ShowContinueError(state,
    1199            0 :                                   format("Zone Exhaust Air Node Name={} is not a zone exhaust node.", state.dataLoopNodes->NodeID(ExhaustNodeNum)));
    1200            0 :                 ShowContinueError(
    1201              :                     state,
    1202            0 :                     format("Check ZoneHVAC:EquipmentConnections for zone={}", state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneName));
    1203            0 :                 ShowContinueError(state, "Zone return air node will be used for ideal loads recirculation air.");
    1204            0 :                 UseReturnNode = true;
    1205              :             } else {
    1206            8 :                 PurchAir.ZoneRecircAirNodeNum = PurchAir.ZoneExhaustAirNodeNum;
    1207              :             }
    1208              :         } else {
    1209            5 :             UseReturnNode = true;
    1210              :         }
    1211           13 :         if (UseReturnNode) {
    1212            5 :             if (state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumReturnNodes == 1) {
    1213            5 :                 PurchAir.ZoneRecircAirNodeNum = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ReturnNode(1);
    1214            0 :             } else if (state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumReturnNodes > 1) {
    1215            0 :                 ShowWarningError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1216            0 :                 ShowContinueError(state,
    1217              :                                   "No Zone Exhaust Air Node Name has been specified for this system and the zone has more than one Return Air Node.");
    1218            0 :                 ShowContinueError(state,
    1219            0 :                                   format("Using the first return air node ={}",
    1220            0 :                                          state.dataLoopNodes->NodeID(state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ReturnNode(1))));
    1221              :             } else {
    1222            0 :                 ShowFatalError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1223            0 :                 ShowContinueError(
    1224              :                     state,
    1225              :                     " Invalid recirculation node. No exhaust or return node has been specified for this zone in ZoneHVAC:EquipmentConnections.");
    1226            0 :                 ShowFatalError(state, "Preceding condition causes termination.");
    1227              :             }
    1228              :         }
    1229              :         // If there is OA and economizer is active, then there must be a limit on cooling flow rate
    1230           13 :         if (PurchAir.OutdoorAir && (PurchAir.EconomizerType != Econ::NoEconomizer)) {
    1231            0 :             if ((PurchAir.CoolingLimit == LimitType::None) || (PurchAir.CoolingLimit == LimitType::Capacity)) {
    1232            0 :                 ShowSevereError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1233            0 :                 ShowContinueError(state, "There is outdoor air with economizer active but there is no limit on cooling air flow rate.");
    1234            0 :                 ShowContinueError(state,
    1235              :                                   "Cooling Limit must be set to LimitFlowRate or LimitFlowRateAndCapacity, and Maximum Cooling Air Flow Rate "
    1236              :                                   "must be set to a value or autosize.");
    1237            0 :                 ShowContinueError(state, "Simulation will proceed with no limit on outdoor air flow rate.");
    1238              :             }
    1239              :         }
    1240              :     }
    1241              : 
    1242          766 :     if (!state.dataGlobal->SysSizingCalc && state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag(PurchAirNum)) {
    1243              : 
    1244           13 :         SizePurchasedAir(state, PurchAirNum);
    1245              : 
    1246           13 :         state.dataPurchasedAirMgr->InitPurchasedAirMySizeFlag(PurchAirNum) = false;
    1247              :     }
    1248              : 
    1249              :     // Do the Begin Environment initializations
    1250          766 :     if (state.dataGlobal->BeginEnvrnFlag && state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum)) {
    1251              : 
    1252           12 :         if ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) {
    1253            0 :             PurchAir.MaxHeatMassFlowRate = state.dataEnvrn->StdRhoAir * PurchAir.MaxHeatVolFlowRate;
    1254              :         } else {
    1255           12 :             PurchAir.MaxHeatMassFlowRate = 0.0;
    1256              :         }
    1257           12 :         if ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
    1258            0 :             PurchAir.MaxCoolMassFlowRate = state.dataEnvrn->StdRhoAir * PurchAir.MaxCoolVolFlowRate;
    1259              :         } else {
    1260           12 :             PurchAir.MaxCoolMassFlowRate = 0.0;
    1261              :         }
    1262           12 :         state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum) = false;
    1263              :     }
    1264              : 
    1265          766 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    1266          730 :         state.dataPurchasedAirMgr->InitPurchasedAirMyEnvrnFlag(PurchAirNum) = true;
    1267              :     }
    1268              : 
    1269          766 :     auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ControlledZoneNum);
    1270              : 
    1271              :     // These initializations are done every iteration
    1272              :     // check that supply air temps can meet the zone thermostat setpoints
    1273          766 :     if (PurchAir.MinCoolSuppAirTemp > zoneTstatSetpt.setptHi && zoneTstatSetpt.setptHi != 0 && PurchAir.CoolingLimit == LimitType::None) {
    1274              :         // Check if the unit is scheduled off
    1275            0 :         UnitOn = true;
    1276              :         //        IF (PurchAir(PurchAirNum)%AvailSchedPtr > 0) THEN
    1277            0 :         if (PurchAir.availSched->getCurrentVal() <= 0) {
    1278            0 :             UnitOn = false;
    1279              :         }
    1280              :         //        END IF
    1281              :         // Check if cooling available
    1282            0 :         bool CoolOn = true;
    1283              :         //        IF (PurchAir(PurchAirNum)%CoolSchedPtr > 0) THEN
    1284            0 :         if (PurchAir.coolAvailSched->getCurrentVal() <= 0) {
    1285            0 :             CoolOn = false;
    1286              :         }
    1287              :         //        END IF
    1288            0 :         if (UnitOn && CoolOn) {
    1289            0 :             if (PurchAir.CoolErrIndex == 0) {
    1290            0 :                 ShowSevereError(state,
    1291            0 :                                 format("InitPurchasedAir: For {} = {} serving Zone {}",
    1292            0 :                                        PurchAir.cObjectName,
    1293            0 :                                        PurchAir.Name,
    1294            0 :                                        state.dataHeatBal->Zone(ControlledZoneNum).Name));
    1295            0 :                 ShowContinueError(state,
    1296            0 :                                   format("..the minimum supply air temperature for cooling [{:.2R}] is greater than the zone cooling mean air "
    1297              :                                          "temperature (MAT) setpoint [{:.2R}].",
    1298            0 :                                          PurchAir.MinCoolSuppAirTemp,
    1299            0 :                                          zoneTstatSetpt.setptHi));
    1300            0 :                 ShowContinueError(state, "..For operative and comfort thermostat controls, the MAT setpoint is computed.");
    1301            0 :                 ShowContinueError(state, "..This error may indicate that the mean radiant temperature or another comfort factor is too warm.");
    1302            0 :                 ShowContinueError(state, "Unit availability is nominally ON and Cooling availability is nominally ON.");
    1303            0 :                 ShowContinueError(state, format("Limit Cooling Capacity Type={}", limitTypeNames[(int)PurchAir.CoolingLimit]));
    1304              :                 // could check for optemp control or comfort control here
    1305            0 :                 ShowContinueErrorTimeStamp(state, "");
    1306              :             }
    1307            0 :             ShowRecurringSevereErrorAtEnd(state,
    1308            0 :                                           "InitPurchasedAir: For " + PurchAir.cObjectName + " = " + PurchAir.Name + " serving Zone " +
    1309            0 :                                               state.dataHeatBal->Zone(ControlledZoneNum).Name +
    1310              :                                               ", the minimum supply air temperature for cooling error continues",
    1311            0 :                                           PurchAir.CoolErrIndex,
    1312            0 :                                           PurchAir.MinCoolSuppAirTemp,
    1313            0 :                                           PurchAir.MinCoolSuppAirTemp,
    1314              :                                           _,
    1315              :                                           "C",
    1316              :                                           "C");
    1317              :         }
    1318              :     }
    1319              : 
    1320          766 :     if (PurchAir.MaxHeatSuppAirTemp < zoneTstatSetpt.setptLo && zoneTstatSetpt.setptLo != 0 && PurchAir.HeatingLimit == LimitType::None) {
    1321              :         // Check if the unit is scheduled off
    1322            0 :         UnitOn = true;
    1323              :         //        IF (PurchAir(PurchAirNum)%AvailSchedPtr > 0) THEN
    1324            0 :         if (PurchAir.availSched->getCurrentVal() <= 0) {
    1325            0 :             UnitOn = false;
    1326              :         }
    1327              :         //        END IF
    1328              :         // Check if heating and cooling available
    1329              : 
    1330            0 :         bool HeatOn = true;
    1331              :         //        IF (PurchAir(PurchAirNum)%HeatSchedPtr > 0) THEN
    1332            0 :         if (PurchAir.heatAvailSched->getCurrentVal() <= 0) {
    1333            0 :             HeatOn = false;
    1334              :         }
    1335              :         //        END IF
    1336            0 :         if (UnitOn && HeatOn) {
    1337            0 :             if (PurchAir.HeatErrIndex == 0) {
    1338            0 :                 ShowSevereMessage(state,
    1339            0 :                                   format("InitPurchasedAir: For {} = {} serving Zone {}",
    1340            0 :                                          PurchAir.cObjectName,
    1341            0 :                                          PurchAir.Name,
    1342            0 :                                          state.dataHeatBal->Zone(ControlledZoneNum).Name));
    1343            0 :                 ShowContinueError(state,
    1344            0 :                                   format("..the maximum supply air temperature for heating [{:.2R}] is less than the zone mean air temperature "
    1345              :                                          "heating setpoint [{:.2R}].",
    1346            0 :                                          PurchAir.MaxHeatSuppAirTemp,
    1347            0 :                                          zoneTstatSetpt.setptLo));
    1348            0 :                 ShowContinueError(state, "..For operative and comfort thermostat controls, the MAT setpoint is computed.");
    1349            0 :                 ShowContinueError(state, "..This error may indicate that the mean radiant temperature or another comfort factor is too cold.");
    1350            0 :                 ShowContinueError(state, "Unit availability is nominally ON and Heating availability is nominally ON.");
    1351            0 :                 ShowContinueError(state, format("Limit Heating Capacity Type={}", limitTypeNames[(int)PurchAir.HeatingLimit]));
    1352              :                 // could check for optemp control or comfort control here
    1353            0 :                 ShowContinueErrorTimeStamp(state, "");
    1354              :             }
    1355            0 :             ShowRecurringSevereErrorAtEnd(state,
    1356            0 :                                           "InitPurchasedAir: For " + PurchAir.cObjectName + " = " + PurchAir.Name + " serving Zone " +
    1357            0 :                                               state.dataHeatBal->Zone(ControlledZoneNum).Name +
    1358              :                                               ", maximum supply air temperature for heating error continues",
    1359            0 :                                           PurchAir.HeatErrIndex,
    1360            0 :                                           PurchAir.MaxHeatSuppAirTemp,
    1361            0 :                                           PurchAir.MaxHeatSuppAirTemp,
    1362              :                                           _,
    1363              :                                           "C",
    1364              :                                           "C");
    1365              :         }
    1366              :     }
    1367              :     //      IF (ErrorsFound .and. .not. WarmupFlag) THEN
    1368              :     //        CALL ShowFatalError(state, 'Preceding conditions cause termination.')
    1369              :     //      ENDIF
    1370          766 : }
    1371              : 
    1372           15 : void SizePurchasedAir(EnergyPlusData &state, int const PurchAirNum)
    1373              : {
    1374              : 
    1375              :     // SUBROUTINE INFORMATION:
    1376              :     //       AUTHOR         Fred Buhl
    1377              :     //       DATE WRITTEN   April 2003
    1378              :     //       MODIFIED       M. Witte, June 2011, add sizing for new capacity fields
    1379              :     //                      August 2013 Daeho Kang, add component sizing table entries
    1380              :     //       RE-ENGINEERED  na
    1381              : 
    1382              :     // PURPOSE OF THIS SUBROUTINE:
    1383              :     // This subroutine is for sizing Purchased Air Components for which flow rates have not been
    1384              :     // specified in the input.
    1385              : 
    1386              :     // METHODOLOGY EMPLOYED:
    1387              :     // Obtains flow rates from the zone sizing arrays.
    1388              : 
    1389              :     // Using/Aliasing
    1390              :     using namespace DataSizing;
    1391              :     using HVAC::CoolingCapacitySizing;
    1392              :     using HVAC::HeatingAirflowSizing;
    1393              :     using HVAC::HeatingCapacitySizing;
    1394              :     using Psychrometrics::CPCW;
    1395              :     using Psychrometrics::CPHW;
    1396              :     using Psychrometrics::PsyCpAirFnW;
    1397              :     using Psychrometrics::PsyHFnTdbW;
    1398              :     using Psychrometrics::RhoH2O;
    1399              : 
    1400              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1401              :     static constexpr std::string_view RoutineName("SizePurchasedAir: "); // include trailing blank space
    1402              : 
    1403              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1404              :     Real64 MaxHeatVolFlowRateDes;     // Autosized maximum heating air flow for reporting
    1405              :     Real64 MaxHeatVolFlowRateUser;    // Hardsized maximum heating air flow for reporting
    1406              :     Real64 MaxCoolVolFlowRateDes;     // Autosized maximum cooling air flow for reporting
    1407              :     Real64 MaxCoolVolFlowRateUser;    // Hardsized maximum cooling air flow for reporting
    1408              :     Real64 MaxHeatSensCapDes;         // Autosized maximum sensible heating capacity for reporting
    1409              :     Real64 MaxHeatSensCapUser;        // Hardsized maximum sensible heating capacity for reporting
    1410              :     Real64 MaxCoolTotCapDes;          // Autosized maximum sensible cooling capacity for reporting
    1411              :     Real64 MaxCoolTotCapUser;         // Hardsized maximum sensible cooling capacity for reporting
    1412           15 :     std::string CompName;             // component name
    1413           15 :     std::string CompType;             // component type
    1414              :     Real64 TempSize;                  // autosized value of coil input field
    1415              :                                       // FractionOfAutosizedHeatingCapacity )
    1416           15 :     Real64 CoolingAirVolFlowDes(0.0); // cooling supply air flow rate
    1417           15 :     Real64 HeatingAirVolFlowDes(0.0); // heating supply air flow rate
    1418              : 
    1419           15 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    1420              : 
    1421           15 :     MaxHeatVolFlowRateDes = 0.0;
    1422           15 :     MaxHeatVolFlowRateUser = 0.0;
    1423           15 :     MaxCoolVolFlowRateDes = 0.0;
    1424           15 :     MaxCoolVolFlowRateUser = 0.0;
    1425           15 :     MaxHeatSensCapDes = 0.0;
    1426           15 :     MaxHeatSensCapUser = 0.0;
    1427           15 :     MaxCoolTotCapDes = 0.0;
    1428           15 :     MaxCoolTotCapUser = 0.0;
    1429              : 
    1430           15 :     state.dataSize->ZoneHeatingOnlyFan = false;
    1431           15 :     state.dataSize->ZoneCoolingOnlyFan = false;
    1432           15 :     CompType = PurchAir.cObjectName;
    1433           15 :     CompName = PurchAir.Name;
    1434              : 
    1435           15 :     if (state.dataSize->CurZoneEqNum > 0) {
    1436           15 :         auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(state.dataSize->CurZoneEqNum);
    1437           15 :         std::string SizingString; // input field sizing description (e.g., Nominal Capacity)
    1438              :         int FieldNum;             // IDD numeric field number where input field description is found
    1439              :         bool PrintFlag;           // TRUE when sizing information is reported in the eio file
    1440           15 :         bool ErrorsFound = false;
    1441           15 :         if (PurchAir.HVACSizingIndex > 0) {
    1442            0 :             state.dataSize->DataZoneNumber = PurchAir.ZonePtr;
    1443            0 :             int zoneHVACIndex = PurchAir.HVACSizingIndex;
    1444              :             int SAFMethod;       // supply air flow rate sizing method (SupplyAirFlowRate, FlowPerFloorArea, FractionOfAutosizedCoolingAirflow,
    1445              :                                  // FractionOfAutosizedHeatingAirflow, HeatingCapacitySizing, etc.)
    1446              :             int CapSizingMethod; // capacity sizing methods (HeatingDesignCapacity, CapacityPerFloorArea, FractionOfAutosizedCoolingCapacity, and
    1447              :             int SizingMethod;    // Integer representation of sizing method name (e.g., CoolingAirflowSizing, HeatingAirflowSizing,
    1448              :                                  // CoolingCapacitySizing)
    1449              : 
    1450            0 :             FieldNum = 5; // N5 , \field Maximum Heating Air Flow Rate
    1451            0 :             PrintFlag = true;
    1452            0 :             SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
    1453            0 :             if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod > 0) {
    1454            0 :                 SizingMethod = HeatingAirflowSizing;
    1455            0 :                 state.dataSize->ZoneHeatingOnlyFan = true;
    1456            0 :                 SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
    1457            0 :                 ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
    1458            0 :                 if (SAFMethod == SupplyAirFlowRate || SAFMethod == FlowPerFloorArea || SAFMethod == FractionOfAutosizedHeatingAirflow) {
    1459            0 :                     if (SAFMethod == SupplyAirFlowRate) {
    1460            0 :                         if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
    1461            0 :                             ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
    1462            0 :                             TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
    1463            0 :                             HeatingAirFlowSizer sizingHeatingAirFlow;
    1464            0 :                             sizingHeatingAirFlow.overrideSizingString(SizingString);
    1465              :                             // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1466            0 :                             sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1467            0 :                             HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
    1468            0 :                         } else {
    1469            0 :                             if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
    1470            0 :                                 HeatingAirFlowSizer sizingHeatingAirFlow;
    1471            0 :                                 sizingHeatingAirFlow.overrideSizingString(SizingString);
    1472              :                                 // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1473            0 :                                 sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1474              :                                 HeatingAirVolFlowDes =
    1475            0 :                                     sizingHeatingAirFlow.size(state, state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow, ErrorsFound);
    1476            0 :                             }
    1477              :                         }
    1478            0 :                     } else if (SAFMethod == FlowPerFloorArea) {
    1479            0 :                         ZoneEqSizing.SystemAirFlow = true;
    1480            0 :                         ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
    1481            0 :                                                   state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1482            0 :                         TempSize = ZoneEqSizing.AirVolFlow;
    1483            0 :                         state.dataSize->DataScalableSizingON = true;
    1484            0 :                         HeatingAirFlowSizer sizingHeatingAirFlow;
    1485            0 :                         sizingHeatingAirFlow.overrideSizingString(SizingString);
    1486              :                         // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1487            0 :                         sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1488            0 :                         HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
    1489            0 :                     } else if (SAFMethod == FractionOfAutosizedHeatingAirflow) {
    1490            0 :                         state.dataSize->DataFracOfAutosizedHeatingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
    1491            0 :                         if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
    1492            0 :                             ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
    1493            0 :                             TempSize = AutoSize;
    1494            0 :                             state.dataSize->DataScalableSizingON = true;
    1495            0 :                             HeatingAirFlowSizer sizingHeatingAirFlow;
    1496            0 :                             sizingHeatingAirFlow.overrideSizingString(SizingString);
    1497              :                             // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1498            0 :                             sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1499            0 :                             HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
    1500            0 :                         }
    1501              : 
    1502              :                     } else {
    1503              :                         // Invalid sizing method
    1504              :                     }
    1505            0 :                 } else if (SAFMethod == FlowPerHeatingCapacity) {
    1506            0 :                     SizingMethod = HeatingCapacitySizing;
    1507            0 :                     TempSize = AutoSize;
    1508            0 :                     PrintFlag = false;
    1509            0 :                     if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow == AutoSize) &&
    1510            0 :                         ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
    1511            0 :                         TempSize = AutoSize;
    1512            0 :                         state.dataSize->DataScalableSizingON = true;
    1513            0 :                         HeatingCapacitySizer sizerHeatingCapacity;
    1514            0 :                         sizerHeatingCapacity.overrideSizingString(SizingString);
    1515            0 :                         sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1516            0 :                         state.dataSize->DataAutosizedHeatingCapacity = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
    1517            0 :                         state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
    1518            0 :                         SizingMethod = HeatingAirflowSizing;
    1519            0 :                         PrintFlag = true;
    1520            0 :                         TempSize = AutoSize;
    1521            0 :                         HeatingAirFlowSizer sizingHeatingAirFlow;
    1522            0 :                         sizingHeatingAirFlow.overrideSizingString(SizingString);
    1523              :                         // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1524            0 :                         sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1525            0 :                         HeatingAirVolFlowDes = sizingHeatingAirFlow.size(state, TempSize, ErrorsFound);
    1526            0 :                     }
    1527              :                 }
    1528            0 :                 MaxHeatVolFlowRateDes = max(0.0, HeatingAirVolFlowDes);
    1529            0 :                 PurchAir.MaxHeatVolFlowRate = MaxHeatVolFlowRateDes;
    1530            0 :                 state.dataSize->ZoneHeatingOnlyFan = false;
    1531              : 
    1532            0 :                 CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
    1533            0 :                 ZoneEqSizing.CapSizingMethod = CapSizingMethod;
    1534            0 :                 if (CapSizingMethod == HeatingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
    1535            0 :                     CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
    1536            0 :                     if (CapSizingMethod == HeatingDesignCapacity) {
    1537            0 :                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity > 0.0) {
    1538            0 :                             ZoneEqSizing.HeatingCapacity = true;
    1539            0 :                             ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1540              :                         }
    1541            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1542            0 :                     } else if (CapSizingMethod == CapacityPerFloorArea) {
    1543            0 :                         ZoneEqSizing.HeatingCapacity = true;
    1544            0 :                         ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
    1545            0 :                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1546            0 :                         state.dataSize->DataScalableSizingON = true;
    1547            0 :                     } else if (CapSizingMethod == FractionOfAutosizedHeatingCapacity) {
    1548            0 :                         state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1549            0 :                         TempSize = AutoSize;
    1550              :                     }
    1551              :                 }
    1552            0 :                 SizingMethod = HeatingCapacitySizing;
    1553            0 :                 SizingString = "";
    1554            0 :                 state.dataSize->ZoneHeatingOnlyFan = true;
    1555            0 :                 PrintFlag = false;
    1556            0 :                 HeatingCapacitySizer sizerHeatingCapacity;
    1557            0 :                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1558            0 :                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1559            0 :                 MaxHeatSensCapDes = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
    1560            0 :                 state.dataSize->ZoneHeatingOnlyFan = false;
    1561            0 :                 if (MaxHeatSensCapDes < HVAC::SmallLoad) {
    1562            0 :                     MaxHeatSensCapDes = 0.0;
    1563              :                 }
    1564            0 :                 if (PurchAir.MaxHeatSensCap > 0.0 && MaxHeatSensCapDes > 0.0) {
    1565            0 :                     MaxHeatSensCapUser = PurchAir.MaxHeatSensCap;
    1566            0 :                     BaseSizer::reportSizerOutput(state,
    1567              :                                                  PurchAir.cObjectName,
    1568              :                                                  PurchAir.Name,
    1569              :                                                  "Design Size Maximum Sensible Heating Capacity [W]",
    1570              :                                                  MaxHeatSensCapDes,
    1571              :                                                  "User-Specified Maximum Sensible Heating Capacity [W]",
    1572              :                                                  MaxHeatSensCapUser);
    1573            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1574            0 :                         if ((std::abs(MaxHeatSensCapDes - MaxHeatSensCapUser) / MaxHeatSensCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
    1575            0 :                             ShowMessage(
    1576              :                                 state,
    1577            0 :                                 format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
    1578            0 :                             ShowContinueError(state, format("...User-Specified Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapUser));
    1579            0 :                             ShowContinueError(
    1580            0 :                                 state, format("...differs from Design Size Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapDes));
    1581            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1582            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1583              :                         }
    1584              :                     }
    1585              :                 }
    1586            0 :             }
    1587              : 
    1588            0 :             PrintFlag = true;
    1589            0 :             if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod > 0) {
    1590            0 :                 state.dataSize->ZoneCoolingOnlyFan = true;
    1591            0 :                 SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingSAFMethod;
    1592            0 :                 ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
    1593            0 :                 if (SAFMethod == SupplyAirFlowRate || SAFMethod == FlowPerFloorArea || SAFMethod == FractionOfAutosizedCoolingAirflow) {
    1594            0 :                     if (SAFMethod == SupplyAirFlowRate) {
    1595            0 :                         if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
    1596            0 :                             ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
    1597            0 :                              (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
    1598            0 :                             TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
    1599            0 :                             CoolingAirFlowSizer sizingCoolingAirFlow;
    1600            0 :                             sizingCoolingAirFlow.overrideSizingString(SizingString);
    1601            0 :                             sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1602            0 :                             CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
    1603            0 :                         } else {
    1604            0 :                             if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow > 0.0) {
    1605            0 :                                 CoolingAirVolFlowDes = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
    1606            0 :                                 CoolingAirFlowSizer sizingCoolingAirFlow;
    1607            0 :                                 sizingCoolingAirFlow.overrideSizingString(SizingString);
    1608            0 :                                 sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1609            0 :                                 CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, CoolingAirVolFlowDes, ErrorsFound);
    1610            0 :                             }
    1611              :                         }
    1612            0 :                     } else if (SAFMethod == FlowPerFloorArea) {
    1613            0 :                         ZoneEqSizing.SystemAirFlow = true;
    1614            0 :                         ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow *
    1615            0 :                                                   state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1616            0 :                         TempSize = ZoneEqSizing.AirVolFlow;
    1617            0 :                         state.dataSize->DataScalableSizingON = true;
    1618            0 :                         CoolingAirFlowSizer sizingCoolingAirFlow;
    1619            0 :                         std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
    1620            0 :                         if (state.dataGlobal->isEpJSON) stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
    1621            0 :                         sizingCoolingAirFlow.overrideSizingString(stringOverride);
    1622              :                         // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1623            0 :                         sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1624            0 :                         CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
    1625            0 :                     } else if (SAFMethod == FractionOfAutosizedCoolingAirflow) {
    1626            0 :                         if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
    1627            0 :                             ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
    1628            0 :                              (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
    1629            0 :                             state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
    1630            0 :                             TempSize = AutoSize;
    1631            0 :                             state.dataSize->DataScalableSizingON = true;
    1632            0 :                             CoolingAirFlowSizer sizingCoolingAirFlow;
    1633            0 :                             std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
    1634            0 :                             if (state.dataGlobal->isEpJSON) stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
    1635            0 :                             sizingCoolingAirFlow.overrideSizingString(stringOverride);
    1636              :                             // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1637            0 :                             sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1638            0 :                             CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
    1639            0 :                         }
    1640              :                     } else {
    1641              :                         // Invalid scalable sizing method
    1642              :                     }
    1643            0 :                 } else if (SAFMethod == FlowPerCoolingCapacity) {
    1644            0 :                     if ((state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow == AutoSize) &&
    1645            0 :                         ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
    1646            0 :                          (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
    1647            0 :                         SizingMethod = CoolingCapacitySizing;
    1648            0 :                         TempSize = AutoSize;
    1649            0 :                         PrintFlag = false;
    1650            0 :                         CoolingCapacitySizer sizerCoolingCapacity;
    1651            0 :                         sizerCoolingCapacity.overrideSizingString(SizingString);
    1652            0 :                         sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1653            0 :                         state.dataSize->DataAutosizedCoolingCapacity = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
    1654            0 :                         state.dataSize->DataFlowPerCoolingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxCoolAirVolFlow;
    1655            0 :                         PrintFlag = true;
    1656            0 :                         TempSize = AutoSize;
    1657            0 :                         state.dataSize->DataScalableSizingON = true;
    1658            0 :                         CoolingAirFlowSizer sizingCoolingAirFlow;
    1659            0 :                         std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
    1660            0 :                         if (state.dataGlobal->isEpJSON) stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
    1661            0 :                         sizingCoolingAirFlow.overrideSizingString(stringOverride);
    1662              :                         // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1663            0 :                         sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1664            0 :                         CoolingAirVolFlowDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
    1665            0 :                     }
    1666              :                 }
    1667            0 :                 MaxCoolVolFlowRateDes = max(0.0, CoolingAirVolFlowDes);
    1668            0 :                 PurchAir.MaxCoolVolFlowRate = MaxCoolVolFlowRateDes;
    1669            0 :                 state.dataSize->ZoneCoolingOnlyFan = false;
    1670            0 :                 state.dataSize->DataScalableSizingON = false;
    1671              : 
    1672            0 :                 CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).CoolingCapMethod;
    1673            0 :                 ZoneEqSizing.CapSizingMethod = CapSizingMethod;
    1674            0 :                 if (CapSizingMethod == CoolingDesignCapacity || CapSizingMethod == CapacityPerFloorArea ||
    1675            0 :                     CapSizingMethod == FractionOfAutosizedCoolingCapacity) {
    1676            0 :                     if (CapSizingMethod == CoolingDesignCapacity) {
    1677            0 :                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity > 0.0) {
    1678            0 :                             ZoneEqSizing.CoolingCapacity = true;
    1679            0 :                             ZoneEqSizing.DesCoolingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
    1680              :                         } else {
    1681            0 :                             state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow;
    1682              :                         }
    1683            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
    1684            0 :                     } else if (CapSizingMethod == CapacityPerFloorArea) {
    1685            0 :                         ZoneEqSizing.CoolingCapacity = true;
    1686            0 :                         ZoneEqSizing.DesCoolingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity *
    1687            0 :                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1688            0 :                         state.dataSize->DataScalableSizingON = true;
    1689            0 :                     } else if (CapSizingMethod == FractionOfAutosizedCoolingCapacity) {
    1690            0 :                         state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledCoolingCapacity;
    1691            0 :                         state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow;
    1692            0 :                         TempSize = AutoSize;
    1693              :                     }
    1694              :                 }
    1695              :                 // SizingMethod = CoolingCapacitySizing;
    1696            0 :                 SizingString = "";
    1697            0 :                 state.dataSize->ZoneCoolingOnlyFan = true;
    1698            0 :                 PrintFlag = false;
    1699            0 :                 TempSize = PurchAir.MaxCoolTotCap;
    1700            0 :                 CoolingCapacitySizer sizerCoolingCapacity;
    1701            0 :                 sizerCoolingCapacity.overrideSizingString(SizingString);
    1702            0 :                 sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1703            0 :                 MaxCoolTotCapDes = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
    1704            0 :                 state.dataSize->ZoneCoolingOnlyFan = false;
    1705            0 :                 if (MaxCoolTotCapDes < HVAC::SmallLoad) {
    1706            0 :                     MaxCoolTotCapDes = 0.0;
    1707              :                 }
    1708            0 :                 if (PurchAir.MaxCoolTotCap > 0.0 && MaxCoolTotCapDes > 0.0) {
    1709            0 :                     MaxCoolTotCapUser = PurchAir.MaxCoolTotCap;
    1710            0 :                     BaseSizer::reportSizerOutput(state,
    1711              :                                                  PurchAir.cObjectName,
    1712              :                                                  PurchAir.Name,
    1713              :                                                  "Design Size Maximum Total Cooling Capacity [W]",
    1714              :                                                  MaxCoolTotCapDes,
    1715              :                                                  "User-Specified Maximum Total Cooling Capacity [W]",
    1716              :                                                  MaxCoolTotCapUser);
    1717            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1718            0 :                         if ((std::abs(MaxCoolTotCapDes - MaxCoolTotCapUser) / MaxCoolTotCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
    1719            0 :                             ShowMessage(
    1720              :                                 state,
    1721            0 :                                 format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
    1722            0 :                             ShowContinueError(state, format("User-Specified Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapUser));
    1723            0 :                             ShowContinueError(state,
    1724            0 :                                               format("differs from Design Size Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapDes));
    1725            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1726            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1727              :                         }
    1728              :                     }
    1729              :                 }
    1730            0 :             }
    1731              : 
    1732              :         } else {
    1733              :             // SizingString = "Maximum Heating Air Flow Rate [m3/s]";
    1734              :             // SizingMethod = HeatingAirflowSizing;
    1735           15 :             FieldNum = 5;
    1736           15 :             SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
    1737           15 :             bool IsAutoSize = false;
    1738           15 :             PrintFlag = true;
    1739           15 :             if ((PurchAir.MaxHeatVolFlowRate == AutoSize) &&
    1740           14 :                 ((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
    1741            2 :                 IsAutoSize = true;
    1742              :             }
    1743           15 :             if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1744            0 :                 if (PurchAir.MaxHeatVolFlowRate > 0.0) {
    1745            0 :                     HeatingAirFlowSizer sizingHeatingAirFlow;
    1746            0 :                     sizingHeatingAirFlow.overrideSizingString(SizingString);
    1747              :                     // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1748            0 :                     sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1749            0 :                     PurchAir.MaxHeatVolFlowRate = sizingHeatingAirFlow.size(state, PurchAir.MaxHeatVolFlowRate, ErrorsFound);
    1750            0 :                 }
    1751            0 :                 MaxHeatVolFlowRateDes = 0.0;
    1752              :             } else {
    1753           15 :                 state.dataSize->ZoneHeatingOnlyFan = true;
    1754           15 :                 TempSize = PurchAir.MaxHeatVolFlowRate;
    1755           15 :                 HeatingAirFlowSizer sizingHeatingAirFlow;
    1756           15 :                 sizingHeatingAirFlow.overrideSizingString(SizingString);
    1757              :                 // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1758           15 :                 sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1759           15 :                 MaxHeatVolFlowRateDes = sizingHeatingAirFlow.size(state, PurchAir.MaxHeatVolFlowRate, ErrorsFound);
    1760           15 :                 PurchAir.MaxHeatVolFlowRate = MaxHeatVolFlowRateDes;
    1761           15 :                 state.dataSize->ZoneHeatingOnlyFan = false;
    1762           15 :             }
    1763              : 
    1764           15 :             IsAutoSize = false;
    1765              :             // SizingMethod = HeatingCapacitySizing;
    1766           15 :             FieldNum = 6; // N6, \field Maximum Sensible Heating Capacity
    1767           15 :             SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
    1768           15 :             if ((PurchAir.MaxHeatSensCap == AutoSize) &&
    1769            2 :                 ((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity))) {
    1770            2 :                 IsAutoSize = true;
    1771              :             }
    1772           15 :             if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1773            0 :                 if (PurchAir.MaxHeatSensCap > 0.0) {
    1774            0 :                     HeatingCapacitySizer sizerHeatingCapacity;
    1775            0 :                     sizerHeatingCapacity.overrideSizingString(SizingString);
    1776            0 :                     sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1777            0 :                     MaxHeatSensCapDes = sizerHeatingCapacity.size(state, PurchAir.MaxHeatSensCap, ErrorsFound);
    1778            0 :                 }
    1779              :             } else {
    1780           15 :                 TempSize = PurchAir.MaxHeatSensCap;
    1781           15 :                 ZoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
    1782           15 :                 state.dataSize->ZoneHeatingOnlyFan = true;
    1783           15 :                 PrintFlag = false;
    1784           15 :                 HeatingCapacitySizer sizerHeatingCapacity;
    1785           15 :                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1786           15 :                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1787           15 :                 MaxHeatSensCapDes = sizerHeatingCapacity.size(state, TempSize, ErrorsFound);
    1788           15 :                 state.dataSize->ZoneHeatingOnlyFan = false;
    1789           15 :             }
    1790           15 :             if (MaxHeatSensCapDes < HVAC::SmallLoad) {
    1791           13 :                 MaxHeatSensCapDes = 0.0;
    1792              :             }
    1793           15 :             if (IsAutoSize) {
    1794            2 :                 PurchAir.MaxHeatSensCap = MaxHeatSensCapDes;
    1795            2 :                 BaseSizer::reportSizerOutput(
    1796              :                     state, PurchAir.cObjectName, PurchAir.Name, "Design Size Maximum Sensible Heating Capacity [W]", MaxHeatSensCapDes);
    1797              :                 // If there is OA, check if sizing calcs have OA>0, throw warning if not
    1798            2 :                 if ((PurchAir.OutdoorAir) && (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA == 0.0)) {
    1799            0 :                     ShowWarningError(state, format("InitPurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1800            0 :                     ShowContinueError(state, "There is outdoor air specified in this object, but the design outdoor air flow rate for this ");
    1801            0 :                     ShowContinueError(state, "zone is zero. The Maximum Sensible Heating Capacity will be autosized for zero outdoor air flow. ");
    1802            0 :                     ShowContinueError(state,
    1803            0 :                                       format("Check the outdoor air specifications in the Sizing:Zone object for zone {}.",
    1804            0 :                                              state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneName));
    1805              :                 }
    1806              :             } else {
    1807           13 :                 if (PurchAir.MaxHeatSensCap > 0.0 && MaxHeatSensCapDes > 0.0) {
    1808            0 :                     MaxHeatSensCapUser = PurchAir.MaxHeatSensCap;
    1809            0 :                     BaseSizer::reportSizerOutput(state,
    1810              :                                                  PurchAir.cObjectName,
    1811              :                                                  PurchAir.Name,
    1812              :                                                  "Design Size Maximum Sensible Heating Capacity [W]",
    1813              :                                                  MaxHeatSensCapDes,
    1814              :                                                  "User-Specified Maximum Sensible Heating Capacity [W]",
    1815              :                                                  MaxHeatSensCapUser);
    1816            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1817            0 :                         if ((std::abs(MaxHeatSensCapDes - MaxHeatSensCapUser) / MaxHeatSensCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
    1818            0 :                             ShowMessage(
    1819              :                                 state,
    1820            0 :                                 format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
    1821            0 :                             ShowContinueError(state, format("...User-Specified Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapUser));
    1822            0 :                             ShowContinueError(
    1823            0 :                                 state, format("...differs from Design Size Maximum Sensible Heating Capacity of {:.2R} [W]", MaxHeatSensCapDes));
    1824            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1825            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1826              :                         }
    1827              :                     }
    1828              :                 }
    1829              :             }
    1830              : 
    1831           15 :             PrintFlag = true;
    1832           15 :             IsAutoSize = false;
    1833           15 :             if ((PurchAir.MaxCoolVolFlowRate == AutoSize) &&
    1834           14 :                 ((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity) ||
    1835           12 :                  (PurchAir.OutdoorAir && PurchAir.EconomizerType != Econ::NoEconomizer))) {
    1836            2 :                 IsAutoSize = true;
    1837              :             }
    1838           15 :             if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1839            0 :                 if (PurchAir.MaxCoolVolFlowRate > 0.0) {
    1840            0 :                     CoolingAirFlowSizer sizingCoolingAirFlow;
    1841            0 :                     std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
    1842            0 :                     if (state.dataGlobal->isEpJSON) stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
    1843            0 :                     sizingCoolingAirFlow.overrideSizingString(stringOverride);
    1844              :                     // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1845            0 :                     sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1846            0 :                     PurchAir.MaxCoolVolFlowRate = sizingCoolingAirFlow.size(state, PurchAir.MaxCoolVolFlowRate, ErrorsFound);
    1847            0 :                 }
    1848              :             } else {
    1849           15 :                 state.dataSize->ZoneCoolingOnlyFan = true;
    1850           15 :                 TempSize = PurchAir.MaxCoolVolFlowRate;
    1851           15 :                 CoolingAirFlowSizer sizingCoolingAirFlow;
    1852           15 :                 std::string stringOverride = "Maximum Cooling Air Flow Rate [m3/s]";
    1853           15 :                 if (state.dataGlobal->isEpJSON) stringOverride = "maximum_cooling_air_flow_rate [m3/s]";
    1854           15 :                 sizingCoolingAirFlow.overrideSizingString(stringOverride);
    1855              :                 // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
    1856           15 :                 sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1857           15 :                 MaxCoolVolFlowRateDes = sizingCoolingAirFlow.size(state, TempSize, ErrorsFound);
    1858           15 :                 PurchAir.MaxCoolVolFlowRate = MaxCoolVolFlowRateDes;
    1859           15 :                 state.dataSize->ZoneCoolingOnlyFan = false;
    1860           15 :             }
    1861              : 
    1862           15 :             IsAutoSize = false;
    1863              :             //  SizingMethod = CoolingCapacitySizing;
    1864           15 :             FieldNum = 8; // N8, \field Maximum Total Cooling Capacity
    1865           15 :             SizingString = state.dataPurchasedAirMgr->PurchAirNumericFields(PurchAirNum).FieldNames(FieldNum) + " [m3/s]";
    1866           15 :             if ((PurchAir.MaxCoolTotCap == AutoSize) &&
    1867            2 :                 ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity))) {
    1868            2 :                 IsAutoSize = true;
    1869              :             }
    1870           15 :             if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1871            0 :                 if (PurchAir.MaxCoolTotCap > 0.0) {
    1872            0 :                     CoolingCapacitySizer sizerCoolingCapacity;
    1873            0 :                     sizerCoolingCapacity.overrideSizingString(SizingString);
    1874            0 :                     sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1875            0 :                     PurchAir.MaxCoolTotCap = sizerCoolingCapacity.size(state, PurchAir.MaxCoolTotCap, ErrorsFound);
    1876            0 :                 }
    1877              :             } else {
    1878           15 :                 state.dataSize->ZoneCoolingOnlyFan = true;
    1879           15 :                 ZoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
    1880           15 :                 PrintFlag = false;
    1881           15 :                 TempSize = PurchAir.MaxCoolTotCap;
    1882           15 :                 CoolingCapacitySizer sizerCoolingCapacity;
    1883           15 :                 sizerCoolingCapacity.overrideSizingString(SizingString);
    1884           15 :                 sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1885           15 :                 MaxCoolTotCapDes = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
    1886           15 :                 state.dataSize->ZoneCoolingOnlyFan = false;
    1887           15 :             }
    1888           15 :             if (MaxCoolTotCapDes < HVAC::SmallLoad) {
    1889           13 :                 MaxCoolTotCapDes = 0.0;
    1890              :             }
    1891           15 :             if (IsAutoSize) {
    1892            2 :                 PurchAir.MaxCoolTotCap = MaxCoolTotCapDes;
    1893            2 :                 BaseSizer::reportSizerOutput(
    1894              :                     state, PurchAir.cObjectName, PurchAir.Name, "Design Size Maximum Total Cooling Capacity [W]", MaxCoolTotCapDes);
    1895              :                 // If there is OA, check if sizing calcs have OA>0, throw warning if not
    1896            2 :                 if ((PurchAir.OutdoorAir) && (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA == 0.0)) {
    1897            0 :                     ShowWarningError(state, format("SizePurchasedAir: In {} = {}", PurchAir.cObjectName, PurchAir.Name));
    1898            0 :                     ShowContinueError(state, "There is outdoor air specified in this object, but the design outdoor air flow rate for this ");
    1899            0 :                     ShowContinueError(state, "zone is zero. The Maximum Total Cooling Capacity will be autosized for zero outdoor air flow. ");
    1900            0 :                     ShowContinueError(state,
    1901            0 :                                       format("Check the outdoor air specifications in the Sizing:Zone object for zone {}.",
    1902            0 :                                              state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).ZoneName));
    1903              :                 }
    1904              :             } else {
    1905           13 :                 if (PurchAir.MaxCoolTotCap > 0.0 && MaxCoolTotCapDes > 0.0) {
    1906            0 :                     MaxCoolTotCapUser = PurchAir.MaxCoolTotCap;
    1907            0 :                     BaseSizer::reportSizerOutput(state,
    1908              :                                                  PurchAir.cObjectName,
    1909              :                                                  PurchAir.Name,
    1910              :                                                  "Design Size Maximum Total Cooling Capacity [W]",
    1911              :                                                  MaxCoolTotCapDes,
    1912              :                                                  "User-Specified Maximum Total Cooling Capacity [W]",
    1913              :                                                  MaxCoolTotCapUser);
    1914            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1915            0 :                         if ((std::abs(MaxCoolTotCapDes - MaxCoolTotCapUser) / MaxCoolTotCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
    1916            0 :                             ShowMessage(
    1917              :                                 state,
    1918            0 :                                 format("SizePurchasedAir: Potential issue with equipment sizing for {} {}", PurchAir.cObjectName, PurchAir.Name));
    1919            0 :                             ShowContinueError(state, format("User-Specified Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapUser));
    1920            0 :                             ShowContinueError(state,
    1921            0 :                                               format("differs from Design Size Maximum Total Cooling Capacity of {:.2R} [W]", MaxCoolTotCapDes));
    1922            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1923            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1924              :                         }
    1925              :                     }
    1926              :                 }
    1927              :             }
    1928              :         }
    1929           15 :     }
    1930           15 : }
    1931              : 
    1932          766 : void CalcPurchAirLoads(EnergyPlusData &state,
    1933              :                        int const PurchAirNum,
    1934              :                        Real64 &SysOutputProvided,   // Sensible output provided [W] cooling = negative
    1935              :                        Real64 &MoistOutputProvided, // Moisture output provided [kg/s] dehumidification = negative
    1936              :                        int const ControlledZoneNum)
    1937              : {
    1938              : 
    1939              :     // SUBROUTINE INFORMATION:
    1940              :     //       AUTHOR         Russ Taylor
    1941              :     //       DATE WRITTEN   Nov 1997
    1942              :     //       MODIFIED       Shirey, Aug 2009 (LatOutputProvided - now MoistOutputProvided)
    1943              :     //                      M. Witte June 2011, add new features including DCV, economizer, dehumidification
    1944              :     //                          and humidification,
    1945              :     //                      July 2012, Chandan Sharma - FSEC: Added hybrid ventilation manager
    1946              :     //       RE-ENGINEERED  na
    1947              : 
    1948              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1949              :     static constexpr std::string_view RoutineName("CalcPurchAirLoads");
    1950              : 
    1951              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1952              :     int InNodeNum; // Ideal loads supply node to zone
    1953              :     //         INTEGER   :: ExhNodeNum        ! Ideal loads exhaust node from zone
    1954              :     int ZoneNodeNum;                   // Zone air node
    1955              :     int OANodeNum;                     // Outdoor air inlet node
    1956              :     int RecircNodeNum;                 // Return air or zone exhaust node
    1957              :     OpMode OperatingMode;              // current operating mode, Off, Heat, Cool, or DeadBand
    1958              :     Real64 SupplyMassFlowRate;         // System supply air mass flow rate [kg/s]
    1959              :     Real64 SupplyMassFlowRateForHumid; // System supply air mass flow rate required to meet humidification load [kg/s]
    1960              :     Real64 SupplyMassFlowRateForDehum; // System supply air mass flow rate required to meet dehumidification load [kg/s]
    1961              :     Real64 SupplyMassFlowRateForCool;  // System supply air mass flow rate required to meet sensible cooling load[kg/s]
    1962              :     Real64 SupplyMassFlowRateForHeat;  // System supply air mass flow rate required to meet sensible heating load[kg/s]
    1963              :     Real64 SupplyHumRatForHumid;       // Supply air humidity ratio require to meet the humidification load [kgWater/kgDryAir]
    1964              :     Real64 SupplyHumRatForDehum;       // Supply air humidity ratio require to meet the dehumidification load [kgWater/kgDryAir]
    1965              :     Real64 OAMassFlowRate;             // Outdoor air mass flow rate [kg/s]
    1966              :     Real64 OAVolFlowRate;              // Outdoor air volume flow rate at standard density [m3/s]
    1967              :     Real64 MinOASensOutput;            // Minimum Outdoor air sensible output [W], <0 means OA is cooler than zone air
    1968              :     Real64 MinOALatOutput;             // Minimum Outdoor air moisture load [kg/s]
    1969              :     Real64 SensOutput;                 // Sensible output [W] (positive means heating, negative means cooling)
    1970              :     Real64 HeatSensOutput;             // Heating sensible output [W]
    1971              :     Real64 CoolSensOutput;             // Cooling sensible output [W] (positive value means cooling)
    1972              :     Real64 LatOutput;                  // Latent output [W] (positive value means humidification, negative means dehumidification)
    1973              :     Real64 CoolLatOutput;              // Cooling latent output [W] (positive value means dehumidification)
    1974              :     Real64 CoolTotOutput;              // Cooling total output [W] (positive value means cooling)
    1975              :     Real64 DeltaT;                     // Delta temperature - reused in multiple places
    1976              :     Real64 DeltaHumRat;                // Delta humidity ratio - reused in multiple places
    1977              :     Real64 QZnHeatSP;                  // Load required to meet heating setpoint [W] (>0 is a heating load)
    1978              :     Real64 QZnCoolSP;                  // Load required to meet cooling setpoint [W] (<0 is a cooling load)
    1979              :     Real64 MdotZnHumidSP;              // Load required to meet humidifying setpoint [kgWater/s] (>0 = a humidify load)
    1980              :     Real64 MdotZnDehumidSP;            // Load required to meet dehumidifying setpoint [kgWater/s] (<0 = a dehumidify load)
    1981              :     bool UnitOn;
    1982              :     bool HeatOn;             // Flag for heating and humidification availability schedule, true if heating is on
    1983              :     bool CoolOn;             // Flag for cooling and dehumidification availability schedule, true if cooling is on
    1984              :     bool EconoOn;            // Flag for economizer operation, true if economizer is on
    1985              :     Real64 SupplyHumRatOrig; // Supply inlet to zone humidity ratio before saturation check [kgWater/kgDryAir]
    1986              :     Real64 SupplyHumRatSat;  // Supply inlet to zone humidity ratio saturation at SupplyTemp [kgWater/kgDryAir]
    1987              :     Real64 SupplyEnthalpy;   // Supply inlet to zone enthalpy [J/kg]
    1988              :     Real64 MixedAirEnthalpy; // Mixed air enthalpy [J/kg]
    1989              :     Real64 CpAir;            // Specific heat [J/kg-C] reused in multiple places
    1990              :     //         REAL(r64) :: SpecHumOut   ! Specific humidity ratio of outlet air (kg moisture / kg moist air)
    1991              :     //         REAL(r64) :: SpecHumIn    ! Specific humidity ratio of inlet [zone] air (kg moisture / kg moist air)
    1992              : 
    1993          766 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    1994              : 
    1995              :     // Sign convention: SysOutputProvided <0 Supply air is heated on entering zone (zone is cooled)
    1996              :     //                  SysOutputProvided >0 Supply air is cooled on entering zone (zone is heated)
    1997          766 :     InNodeNum = PurchAir.ZoneSupplyAirNodeNum;
    1998          766 :     ZoneNodeNum = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
    1999          766 :     OANodeNum = PurchAir.OutdoorAirNodeNum;
    2000          766 :     RecircNodeNum = PurchAir.ZoneRecircAirNodeNum;
    2001          766 :     SupplyMassFlowRate = 0.0;
    2002          766 :     OAMassFlowRate = 0.0;
    2003          766 :     PurchAir.MinOAMassFlowRate = 0.0;
    2004          766 :     PurchAir.TimeEconoActive = 0.0;
    2005          766 :     PurchAir.TimeHtRecActive = 0.0;
    2006          766 :     SysOutputProvided = 0.0;
    2007          766 :     MoistOutputProvided = 0.0;
    2008          766 :     CoolSensOutput = 0.0;
    2009          766 :     CoolLatOutput = 0.0;
    2010          766 :     CoolTotOutput = 0.0;
    2011          766 :     HeatSensOutput = 0.0;
    2012          766 :     LatOutput = 0.0;
    2013              : 
    2014              :     // default unit to ON
    2015          766 :     UnitOn = true;
    2016          766 :     EconoOn = false;
    2017              :     // get current zone requirements
    2018          766 :     QZnHeatSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToHeatSP;
    2019          766 :     QZnCoolSP = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ControlledZoneNum).RemainingOutputReqToCoolSP;
    2020              : 
    2021          766 :     if (allocated(state.dataAvail->ZoneComp)) {
    2022          752 :         auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::PurchasedAir).ZoneCompAvailMgrs(PurchAirNum);
    2023          752 :         availMgr.ZoneNum = ControlledZoneNum;
    2024          752 :         PurchAir.availStatus = availMgr.availStatus;
    2025              :         // Check if the hybrid ventilation availability manager is turning the unit off
    2026          752 :         if (PurchAir.availStatus == Avail::Status::ForceOff) {
    2027            0 :             UnitOn = false;
    2028              :         }
    2029              :     }
    2030              : 
    2031              :     // Check if the unit is scheduled off
    2032          766 :     if (PurchAir.availSched->getCurrentVal() <= 0) {
    2033            0 :         UnitOn = false;
    2034              :     }
    2035              :     //         END IF
    2036              :     // Check if heating and cooling available
    2037          766 :     HeatOn = true;
    2038          766 :     if (PurchAir.heatAvailSched->getCurrentVal() <= 0) {
    2039            0 :         HeatOn = false;
    2040              :     }
    2041              : 
    2042          766 :     CoolOn = true;
    2043          766 :     if (PurchAir.coolAvailSched->getCurrentVal() <= 0) {
    2044            0 :         CoolOn = false;
    2045              :     }
    2046              :     //         END IF
    2047              : 
    2048          766 :     if (UnitOn) {
    2049          766 :         auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ControlledZoneNum);
    2050              :         // Calculate current minimum outdoor air flow rate based on design OA specifications and DCV or CO2 control
    2051          766 :         CalcPurchAirMinOAMassFlow(state, PurchAirNum, ControlledZoneNum, OAMassFlowRate);
    2052              : 
    2053              :         // EMS override point  Purch air outdoor air massflow rate.....
    2054          766 :         if (PurchAir.EMSOverrideOAMdotOn) {
    2055            0 :             OAMassFlowRate = PurchAir.EMSValueOAMassFlowRate;
    2056              :         }
    2057              : 
    2058              :         // Calculate minimum outdoor air sensible and latent load
    2059          766 :         if (PurchAir.OutdoorAir) {
    2060            1 :             CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(OANodeNum).HumRat);
    2061            1 :             MinOASensOutput = OAMassFlowRate * CpAir * (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2062            1 :             MinOALatOutput = OAMassFlowRate * (state.dataLoopNodes->Node(OANodeNum).HumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    2063              :         } else {
    2064          765 :             MinOASensOutput = 0.0;
    2065          765 :             MinOALatOutput = 0.0;
    2066              :         }
    2067              :         // SupplyMassFlowRate = OAMassFlowRate;
    2068              : 
    2069              :         // Check if cooling of the supply air stream is required
    2070              : 
    2071              :         // Cooling operation
    2072          766 :         if ((MinOASensOutput >= QZnCoolSP) && (state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleHeat)) {
    2073          358 :             OperatingMode = OpMode::Cool;
    2074              :             // Calculate supply mass flow, temp and humidity with the following constraints:
    2075              :             //  Min cooling supply temp
    2076              :             //  Max total cooling capacity
    2077              :             //  Max cooling airflow
    2078              :             //  Min cooling supply humrat  (and Max heating supply humrat)
    2079              :             //  Min OA mass flow rate
    2080              : 
    2081              :             // Check if OA flow rate greater than max cooling airflow limit
    2082          358 :             if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
    2083            0 :                 (OAMassFlowRate > PurchAir.MaxCoolMassFlowRate)) {
    2084            0 :                 OAVolFlowRate = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
    2085            0 :                 if (PurchAir.OAFlowMaxCoolOutputError < 1) {
    2086            0 :                     ++PurchAir.OAFlowMaxCoolOutputError;
    2087            0 :                     ShowWarningError(state,
    2088            0 :                                      format("{} \"{}\" Requested outdoor air flow rate = {:.5T} [m3/s] exceeds limit.",
    2089            0 :                                             PurchAir.cObjectName,
    2090            0 :                                             PurchAir.Name,
    2091              :                                             OAVolFlowRate));
    2092            0 :                     ShowContinueError(state,
    2093            0 :                                       format(" Will be reduced to the Maximum Cooling Air Flow Rate = {:.5T} [m3/s]", PurchAir.MaxCoolVolFlowRate));
    2094            0 :                     ShowContinueErrorTimeStamp(state, "");
    2095              :                 } else {
    2096            0 :                     ShowRecurringWarningErrorAtEnd(
    2097              :                         state,
    2098            0 :                         PurchAir.cObjectName + " \"" + PurchAir.Name +
    2099              :                             "\" Requested outdoor air flow rate [m3/s] reduced to Maximum Cooling Air Flow Rate warning continues...",
    2100            0 :                         PurchAir.OAFlowMaxCoolOutputIndex,
    2101              :                         OAVolFlowRate);
    2102              :                 }
    2103            0 :                 OAMassFlowRate = PurchAir.MaxCoolMassFlowRate;
    2104              : 
    2105              :             } else {
    2106              :                 // Model economizer
    2107          358 :                 if (PurchAir.EconomizerType != Econ::NoEconomizer) {
    2108            0 :                     if (((PurchAir.EconomizerType == Econ::DifferentialDryBulb) &&
    2109            0 :                          (state.dataLoopNodes->Node(OANodeNum).Temp < state.dataLoopNodes->Node(PurchAir.ZoneRecircAirNodeNum).Temp)) ||
    2110            0 :                         ((PurchAir.EconomizerType == Econ::DifferentialEnthalpy) &&
    2111            0 :                          (state.dataLoopNodes->Node(OANodeNum).Enthalpy < state.dataLoopNodes->Node(PurchAir.ZoneRecircAirNodeNum).Enthalpy))) {
    2112              : 
    2113              :                         // Calculate supply MassFlowRate based on sensible load but limit to Max Cooling Supply Air Flow Rate if specified
    2114            0 :                         CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2115            0 :                         DeltaT = (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2116            0 :                         if (DeltaT < -HVAC::SmallTempDiff) {
    2117            0 :                             SupplyMassFlowRate = QZnCoolSP / CpAir / DeltaT;
    2118            0 :                             if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
    2119            0 :                                 (PurchAir.MaxCoolMassFlowRate > 0.0)) {
    2120            0 :                                 SupplyMassFlowRate = min(max(SupplyMassFlowRate, 0.0), PurchAir.MaxCoolMassFlowRate);
    2121              :                             }
    2122            0 :                             if (SupplyMassFlowRate > OAMassFlowRate) {
    2123            0 :                                 EconoOn = true;
    2124            0 :                                 OAMassFlowRate = SupplyMassFlowRate;
    2125            0 :                                 PurchAir.TimeEconoActive = state.dataHVACGlobal->TimeStepSys;
    2126              :                             }
    2127              :                         }
    2128              :                     }
    2129              :                 }
    2130              :             }
    2131              : 
    2132              :             // Determine supply mass flow rate
    2133              :             // Mass flow rate to meet sensible load, at Minimum Cooling Supply Air Temperature
    2134          358 :             SupplyMassFlowRateForCool = 0.0;
    2135          358 :             if (CoolOn) {
    2136          358 :                 CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2137          358 :                 DeltaT = (PurchAir.MinCoolSuppAirTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2138          358 :                 if (DeltaT < -HVAC::SmallTempDiff) {
    2139          358 :                     SupplyMassFlowRateForCool = QZnCoolSP / CpAir / DeltaT;
    2140              :                 }
    2141              :             }
    2142              : 
    2143              :             // Mass flow rate to meet dehumidification load, if applicable, at Minimum Cooling Supply Humidity Ratio
    2144          358 :             SupplyMassFlowRateForDehum = 0.0;
    2145          358 :             if (CoolOn) {
    2146          358 :                 if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
    2147            1 :                     MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
    2148            1 :                     DeltaHumRat = (PurchAir.MinCoolSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    2149            1 :                     if ((DeltaHumRat < -SmallDeltaHumRat) && (MdotZnDehumidSP < 0.0)) {
    2150            1 :                         SupplyMassFlowRateForDehum = MdotZnDehumidSP / DeltaHumRat;
    2151              :                     }
    2152              :                 }
    2153              :             }
    2154              : 
    2155              :             // Mass flow rate to meet humidification load, if applicable, at Maximum Heating Supply Humidity Ratio
    2156              :             // This section is the cooling section, so humidification should activate only if humidification control = humidistat
    2157              :             //   and if dehumidification control = humidistat or none
    2158          358 :             SupplyMassFlowRateForHumid = 0.0;
    2159          358 :             if (HeatOn) {
    2160          358 :                 if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
    2161            0 :                     if ((PurchAir.DehumidCtrlType == HumControl::Humidistat) || (PurchAir.DehumidCtrlType == HumControl::None)) {
    2162            0 :                         MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
    2163            0 :                         DeltaHumRat = (PurchAir.MaxHeatSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    2164            0 :                         if ((DeltaHumRat > SmallDeltaHumRat) && (MdotZnHumidSP > 0.0)) {
    2165            0 :                             SupplyMassFlowRateForHumid = MdotZnHumidSP / DeltaHumRat;
    2166              :                         }
    2167              :                     }
    2168              :                 }
    2169              :             }
    2170              : 
    2171              :             // If cooling capacity is limited to zero, SupplyMassFlowRate* should be set to zero
    2172          358 :             if (((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
    2173            0 :                 (PurchAir.MaxCoolTotCap == 0)) {
    2174            0 :                 SupplyMassFlowRateForCool = 0;
    2175            0 :                 SupplyMassFlowRateForDehum = 0;
    2176            0 :                 SupplyMassFlowRateForHumid = 0;
    2177              :             }
    2178              : 
    2179              :             // Supply mass flow is greatest of these, but limit to cooling max flow rate, if applicable
    2180          358 :             SupplyMassFlowRate = max(0.0, OAMassFlowRate, SupplyMassFlowRateForCool, SupplyMassFlowRateForDehum, SupplyMassFlowRateForHumid);
    2181              :             // EMS override point  Purch air massflow rate..... but only if unit is on, i.e. SupplyMassFlowRate>0.0
    2182          358 :             if (PurchAir.EMSOverrideMdotOn) {
    2183            1 :                 SupplyMassFlowRate = PurchAir.EMSValueMassFlowRate;
    2184            1 :                 OAMassFlowRate = min(OAMassFlowRate, SupplyMassFlowRate);
    2185              :             }
    2186          358 :             if (((PurchAir.CoolingLimit == LimitType::FlowRate) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) &&
    2187            0 :                 (PurchAir.MaxCoolMassFlowRate > 0.0)) {
    2188            0 :                 SupplyMassFlowRate = min(SupplyMassFlowRate, PurchAir.MaxCoolMassFlowRate);
    2189              :             }
    2190              : 
    2191          358 :             if (SupplyMassFlowRate <= HVAC::VerySmallMassFlow) SupplyMassFlowRate = 0.0;
    2192              : 
    2193              :             // Calculate mixed air conditions
    2194          358 :             CalcPurchAirMixedAir(state,
    2195              :                                  PurchAirNum,
    2196              :                                  OAMassFlowRate,
    2197              :                                  SupplyMassFlowRate,
    2198          358 :                                  PurchAir.MixedAirTemp,
    2199          358 :                                  PurchAir.MixedAirHumRat,
    2200              :                                  MixedAirEnthalpy,
    2201              :                                  OperatingMode);
    2202              : 
    2203              :             // Calculate supply air conditions using final massflow rate, imposing capacity limits if specified
    2204              :             // If capacity limits are exceeded, keep massflow rate where it is and adjust supply temp
    2205              :             // In general, in the cooling section, don't let SupplyTemp be set to something that results in heating
    2206          358 :             if (SupplyMassFlowRate > 0.0) {
    2207              :                 // Calculate supply temp at SupplyMassFlowRate and recheck limit on Minimum Cooling Supply Air Temperature
    2208          358 :                 CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2209          358 :                 PurchAir.SupplyTemp = QZnCoolSP / (CpAir * SupplyMassFlowRate) + state.dataLoopNodes->Node(ZoneNodeNum).Temp;
    2210          358 :                 PurchAir.SupplyTemp = max(PurchAir.SupplyTemp, PurchAir.MinCoolSuppAirTemp);
    2211              :                 // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
    2212          358 :                 PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2213          358 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2214          358 :                 SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2215              : 
    2216              :                 // Check sensible load vs max total cooling capacity, if specified, and adjust supply temp before applying humidity controls
    2217              :                 // Will check again later, too
    2218          358 :                 if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
    2219            0 :                     CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2220            0 :                     CoolSensOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
    2221            0 :                     if (CoolSensOutput >= PurchAir.MaxCoolTotCap) {
    2222            0 :                         CoolSensOutput = PurchAir.MaxCoolTotCap;
    2223            0 :                         SupplyEnthalpy = MixedAirEnthalpy - CoolSensOutput / SupplyMassFlowRate;
    2224            0 :                         PurchAir.SupplyTemp = PsyTdbFnHW(SupplyEnthalpy, PurchAir.SupplyHumRat);
    2225              :                         // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
    2226            0 :                         PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2227              :                     } // Capacity limit exceeded
    2228              :                 }
    2229              : 
    2230              :                 // Set supply humidity ratio for cooling/dehumidification
    2231          358 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2232          358 :                 switch (PurchAir.DehumidCtrlType) {
    2233            0 :                 case HumControl::None: {
    2234            0 :                     PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat; // Unnecessary line?
    2235            0 :                 } break;
    2236            0 :                 case HumControl::ConstantSensibleHeatRatio: {
    2237              :                     // SHR = CoolSensOutput/CoolTotOutput
    2238              :                     // CoolTotOutput = CoolSensOutput/SHR
    2239            0 :                     CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2240            0 :                     CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
    2241            0 :                     CoolTotOutput = CoolSensOutput / PurchAir.CoolSHR;
    2242            0 :                     SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
    2243              :                     //  Limit for overdrying (avoid Pysch errors which occur if SupplyEnthalpy is too low for SupplyTemp)
    2244            0 :                     SupplyEnthalpy = max(SupplyEnthalpy, PsyHFnTdbW(PurchAir.SupplyTemp, 0.00001));
    2245            0 :                     PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName));
    2246              :                     // Apply min cooling humidity ratio limit
    2247            0 :                     PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, PurchAir.MinCoolSuppAirHumRat);
    2248              :                     // But don't let it be higher than incoming MixedAirHumRat
    2249            0 :                     PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, PurchAir.MixedAirHumRat);
    2250            0 :                 } break;
    2251            1 :                 case HumControl::Humidistat: {
    2252            1 :                     MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
    2253            1 :                     SupplyHumRatForDehum = MdotZnDehumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
    2254            1 :                     SupplyHumRatForDehum = max(SupplyHumRatForDehum, PurchAir.MinCoolSuppAirHumRat);
    2255            1 :                     PurchAir.SupplyHumRat = min(PurchAir.MixedAirHumRat, SupplyHumRatForDehum);
    2256            1 :                 } break;
    2257          357 :                 case HumControl::ConstantSupplyHumidityRatio: {
    2258          357 :                     PurchAir.SupplyHumRat = PurchAir.MinCoolSuppAirHumRat;
    2259          357 :                 } break;
    2260            0 :                 default: {
    2261            0 :                     PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2262            0 :                 } break;
    2263              :                 }
    2264              : 
    2265              :                 // Check supply humidity ratio for humidification (SupplyHumRatForHum should always be < SupplyHumRatForDehum)
    2266              :                 // This section is the cooling section, so humidification should activate only if humidification control = humidistat
    2267              :                 //   and if dehumidification control = humidistat or none
    2268          358 :                 if (HeatOn) {
    2269          358 :                     if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
    2270            0 :                         if ((PurchAir.DehumidCtrlType == HumControl::Humidistat) || (PurchAir.DehumidCtrlType == HumControl::None)) {
    2271            0 :                             MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
    2272            0 :                             SupplyHumRatForHumid = MdotZnHumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
    2273            0 :                             SupplyHumRatForHumid = min(SupplyHumRatForHumid, PurchAir.MaxHeatSuppAirHumRat);
    2274            0 :                             PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, SupplyHumRatForHumid);
    2275              :                         }
    2276              :                     }
    2277              :                 }
    2278              : 
    2279              :                 //   Limit supply humidity ratio to saturation at supply outlet temp
    2280              : 
    2281          358 :                 SupplyHumRatOrig = PurchAir.SupplyHumRat;
    2282          358 :                 SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
    2283          358 :                 PurchAir.SupplyHumRat = min(SupplyHumRatOrig, SupplyHumRatSat);
    2284          358 :                 SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2285              : 
    2286              :                 // Check max total Cooling capacity, if specified
    2287          358 :                 if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
    2288              :                     // If dehumidifying, compare total cooling to the limit
    2289            0 :                     if (PurchAir.SupplyHumRat < PurchAir.MixedAirHumRat) { // Dehumidifying
    2290            0 :                         CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
    2291            0 :                         if ((CoolTotOutput) > PurchAir.MaxCoolTotCap) {
    2292            0 :                             CoolTotOutput = PurchAir.MaxCoolTotCap;
    2293            0 :                             SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
    2294              :                             // Adjust output based on dehumidification control type
    2295            0 :                             switch (PurchAir.DehumidCtrlType) {
    2296            0 :                             case HumControl::ConstantSensibleHeatRatio: {
    2297              :                                 // Adjust both supply temp and humidity ratio to maintain SHR
    2298              :                                 // SHR = CoolSensOutput/CoolTotOutput
    2299              :                                 // CoolSensOutput = SHR*CoolTotOutput
    2300            0 :                                 CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2301            0 :                                 CoolSensOutput = CoolTotOutput * PurchAir.CoolSHR;
    2302            0 :                                 PurchAir.SupplyTemp = PurchAir.MixedAirTemp - CoolSensOutput / (CpAir * SupplyMassFlowRate);
    2303              :                                 // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
    2304            0 :                                 PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2305              :                                 //  Limit for overdrying (avoid Pysch errors which occur if SupplyEnthalpy is too low for SupplyTemp)
    2306            0 :                                 SupplyEnthalpy = max(SupplyEnthalpy, PsyHFnTdbW(PurchAir.SupplyTemp, 0.00001));
    2307            0 :                                 PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2308            0 :                             } break;
    2309            0 :                             case HumControl::Humidistat: {
    2310              :                                 // Keep supply temp and adjust humidity ratio to reduce load
    2311            0 :                                 PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2312            0 :                             } break;
    2313            0 :                             case HumControl::None:
    2314              :                             case HumControl::ConstantSupplyHumidityRatio: {
    2315              :                                 // Keep humidity ratio and adjust supply temp
    2316              :                                 // Check if latent output exceeds capacity
    2317            0 :                                 CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2318            0 :                                 CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
    2319            0 :                                 CoolLatOutput = CoolTotOutput - CoolSensOutput;
    2320            0 :                                 if (CoolLatOutput >= PurchAir.MaxCoolTotCap) {
    2321            0 :                                     PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
    2322            0 :                                     PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2323            0 :                                     CoolLatOutput = PurchAir.MaxCoolTotCap;
    2324              :                                 } else {
    2325            0 :                                     PurchAir.SupplyTemp = PsyTdbFnHW(SupplyEnthalpy, PurchAir.SupplyHumRat);
    2326              :                                     // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
    2327            0 :                                     PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2328              :                                 }
    2329            0 :                             } break;
    2330            0 :                             default:
    2331            0 :                                 break;
    2332              :                             }
    2333              :                             // Limit supply humidity ratio to saturation at supply outlet temp
    2334              :                             // If saturation exceeded, then honor capacity limit and set to dew point at supply enthalpy
    2335              : 
    2336            0 :                             SupplyHumRatOrig = PurchAir.SupplyHumRat;
    2337            0 :                             SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
    2338            0 :                             if (SupplyHumRatSat < SupplyHumRatOrig) {
    2339            0 :                                 PurchAir.SupplyTemp = PsyTsatFnHPb(state, SupplyEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName);
    2340              : 
    2341              :                                 // This is the cooling mode, so SupplyTemp can't be more than MixedAirTemp
    2342            0 :                                 PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2343            0 :                                 PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2344            0 :                                 SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2345              :                                 // CpAir = PsyCpAirFnW(MixedAirHumRat)
    2346              :                                 // CoolSensOutput = SupplyMassFlowRate * CpAir * (MixedAirTemp - SupplyTemp)
    2347              :                                 // CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy)
    2348              :                             }
    2349              :                         }    // Capacity limit exceeded
    2350              :                     } else { // Not dehumidifying
    2351              :                         // If not dehumidifying, compare sensible cooling to the limit
    2352              :                         // This section will only increase supply temp, so no need to recheck for super-saturation
    2353            0 :                         CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2354            0 :                         CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
    2355            0 :                         if (CoolSensOutput >= PurchAir.MaxCoolTotCap) {
    2356            0 :                             CoolSensOutput = PurchAir.MaxCoolTotCap;
    2357            0 :                             PurchAir.SupplyTemp = PurchAir.MixedAirTemp - CoolSensOutput / (SupplyMassFlowRate * CpAir);
    2358              :                         } // Capacity limit exceeded
    2359              :                     }     // Dehumidifying or not
    2360              :                 }         // Capacity limit active
    2361              : 
    2362              :             } else { // SupplyMassFlowRate is zero
    2363            0 :                 SupplyEnthalpy = MixedAirEnthalpy;
    2364            0 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2365            0 :                 PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
    2366            0 :                 CoolSensOutput = 0.0;
    2367            0 :                 CoolTotOutput = 0.0;
    2368              :             }
    2369              :             // Heating or no-load operation
    2370              :         } else { // Heating or no-load case
    2371          408 :             if ((MinOASensOutput < QZnHeatSP) && (state.dataHeatBalFanSys->TempControlType(ControlledZoneNum) != HVAC::SetptType::SingleCool)) {
    2372          276 :                 OperatingMode = OpMode::Heat;
    2373              :             } else { // DeadBand mode shuts off heat recovery and economizer
    2374          132 :                 OperatingMode = OpMode::DeadBand;
    2375              :             }
    2376              :             // Calculate supply mass flow, temp and humidity with the following constraints:
    2377              :             //  Max heating supply temp
    2378              :             //  Max sensible heating capacity
    2379              :             //  Max heating airflow
    2380              :             //  Max heating supply humrat (and Min cooling supply humrat)
    2381              :             //  Min OA mass flow rate
    2382              : 
    2383              :             // Check if OA flow rate greater than max heating airflow limit
    2384          408 :             if (((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
    2385            0 :                 (OAMassFlowRate > PurchAir.MaxHeatMassFlowRate)) {
    2386            0 :                 OAVolFlowRate = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
    2387            0 :                 if (PurchAir.OAFlowMaxHeatOutputError < 1) {
    2388            0 :                     ++PurchAir.OAFlowMaxHeatOutputError;
    2389            0 :                     ShowWarningError(state,
    2390            0 :                                      format("{} \"{}\" Requested outdoor air flow rate = {:.5T} [m3/s] exceeds limit.",
    2391            0 :                                             PurchAir.cObjectName,
    2392            0 :                                             PurchAir.Name,
    2393              :                                             OAVolFlowRate));
    2394            0 :                     ShowContinueError(state,
    2395            0 :                                       format(" Will be reduced to the Maximum Heating Air Flow Rate = {:.5T} [m3/s]", PurchAir.MaxHeatVolFlowRate));
    2396            0 :                     ShowContinueErrorTimeStamp(state, "");
    2397              :                 } else {
    2398            0 :                     ShowRecurringWarningErrorAtEnd(
    2399              :                         state,
    2400            0 :                         PurchAir.cObjectName + " \"" + PurchAir.Name +
    2401              :                             "\" Requested outdoor air flow rate [m3/s] reduced to Maximum Heating Air Flow Rate warning continues...",
    2402            0 :                         PurchAir.OAFlowMaxHeatOutputIndex,
    2403              :                         OAVolFlowRate);
    2404              :                 }
    2405            0 :                 OAMassFlowRate = PurchAir.MaxHeatMassFlowRate;
    2406              :             }
    2407              : 
    2408              :             // SupplyMassFlowRate = OAMassFlowRate;
    2409              : 
    2410              :             // Determine supply mass flow rate
    2411              :             // Mass flow rate to meet sensible load, at Minimum Cooling Supply Air Temperature
    2412          408 :             SupplyMassFlowRateForHeat = 0.0;
    2413          408 :             if ((HeatOn) && (OperatingMode == OpMode::Heat)) {
    2414          276 :                 CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2415          276 :                 DeltaT = (PurchAir.MaxHeatSuppAirTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2416          276 :                 if (DeltaT > HVAC::SmallTempDiff) {
    2417          276 :                     SupplyMassFlowRateForHeat = QZnHeatSP / CpAir / DeltaT;
    2418              :                 }
    2419              :             }
    2420              : 
    2421              :             // Mass flow rate to meet dehumidification load, if applicable, at Minimum Cooling Supply Humidity Ratio
    2422              :             // This section is the heating/deadband section, so dehumidification should activate
    2423              :             //   only if dehumidification control = humidistat
    2424              :             //   and if humidification control = humidistat or none or if operating in deadband mode
    2425          408 :             SupplyMassFlowRateForDehum = 0.0;
    2426          408 :             if (CoolOn) {
    2427          408 :                 if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
    2428            0 :                     if ((PurchAir.HumidCtrlType == HumControl::Humidistat) || (PurchAir.HumidCtrlType == HumControl::None) ||
    2429              :                         (OperatingMode == OpMode::DeadBand)) {
    2430            0 :                         MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
    2431            0 :                         DeltaHumRat = (PurchAir.MinCoolSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    2432            0 :                         if ((DeltaHumRat < -SmallDeltaHumRat) && (MdotZnDehumidSP < 0.0)) {
    2433            0 :                             SupplyMassFlowRateForDehum = MdotZnDehumidSP / DeltaHumRat;
    2434              :                         }
    2435              :                     }
    2436              :                 }
    2437              :             }
    2438              : 
    2439              :             // Mass flow rate to meet humidification load, if applicable, at Maximum Heating Supply Humidity Ratio
    2440          408 :             SupplyMassFlowRateForHumid = 0.0;
    2441          408 :             if (HeatOn) {
    2442          408 :                 if (PurchAir.HumidCtrlType == HumControl::Humidistat) {
    2443            0 :                     MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
    2444            0 :                     DeltaHumRat = (PurchAir.MaxHeatSuppAirHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat);
    2445            0 :                     if ((DeltaHumRat > SmallDeltaHumRat) && (MdotZnHumidSP > 0.0)) {
    2446            0 :                         SupplyMassFlowRateForHumid = MdotZnHumidSP / DeltaHumRat;
    2447              :                     }
    2448              :                 }
    2449              :             }
    2450              : 
    2451              :             // If heating capacity is limited to zero, SupplyMassFlowRate* should be set to zero
    2452          408 :             if (((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
    2453            2 :                 (PurchAir.MaxHeatSensCap == 0)) {
    2454            2 :                 SupplyMassFlowRateForHeat = 0;
    2455            2 :                 SupplyMassFlowRateForDehum = 0;
    2456            2 :                 SupplyMassFlowRateForHumid = 0;
    2457              :             }
    2458              : 
    2459              :             // Supply mass flow is greatest of these, but limit to heating max flow rate, if applicable
    2460          408 :             SupplyMassFlowRate = max(0.0, OAMassFlowRate, SupplyMassFlowRateForHeat, SupplyMassFlowRateForDehum, SupplyMassFlowRateForHumid);
    2461              :             // EMS override point  Purch air massflow rate..... but only if unit is on, i.e. SupplyMassFlowRate>0.0
    2462          408 :             if (PurchAir.EMSOverrideMdotOn) {
    2463            3 :                 SupplyMassFlowRate = PurchAir.EMSValueMassFlowRate;
    2464            3 :                 OAMassFlowRate = min(OAMassFlowRate, SupplyMassFlowRate);
    2465              :             }
    2466          408 :             if (((PurchAir.HeatingLimit == LimitType::FlowRate) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) &&
    2467            0 :                 (PurchAir.MaxHeatMassFlowRate > 0.0)) {
    2468            0 :                 SupplyMassFlowRate = min(SupplyMassFlowRate, PurchAir.MaxHeatMassFlowRate);
    2469              :             }
    2470              : 
    2471          408 :             if (SupplyMassFlowRate <= HVAC::VerySmallMassFlow) SupplyMassFlowRate = 0.0;
    2472              : 
    2473              :             // Calculate mixed air conditions
    2474          408 :             CalcPurchAirMixedAir(state,
    2475              :                                  PurchAirNum,
    2476              :                                  OAMassFlowRate,
    2477              :                                  SupplyMassFlowRate,
    2478          408 :                                  PurchAir.MixedAirTemp,
    2479          408 :                                  PurchAir.MixedAirHumRat,
    2480              :                                  MixedAirEnthalpy,
    2481              :                                  OperatingMode);
    2482              : 
    2483              :             // Calculate supply air conditions using final massflow rate, imposing capacity limits if specified
    2484              :             // If capacity limits are exceeded, keep massflow rate where it is and adjust supply temp
    2485          408 :             if (SupplyMassFlowRate > 0.0) {
    2486          275 :                 if ((HeatOn) && (OperatingMode == OpMode::Heat)) {
    2487              :                     // Calculate supply temp at SupplyMassFlowRate and check limit on Maximum Heating Supply Air Temperature
    2488          274 :                     CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2489          274 :                     PurchAir.SupplyTemp = QZnHeatSP / (CpAir * SupplyMassFlowRate) + state.dataLoopNodes->Node(ZoneNodeNum).Temp;
    2490          274 :                     PurchAir.SupplyTemp = min(PurchAir.SupplyTemp, PurchAir.MaxHeatSuppAirTemp);
    2491              :                     // This is the heating mode, so SupplyTemp can't be less than MixedAirTemp
    2492          274 :                     PurchAir.SupplyTemp = max(PurchAir.SupplyTemp, PurchAir.MixedAirTemp);
    2493              :                     // Check max heating capacity, if specified
    2494          274 :                     if ((PurchAir.HeatingLimit == LimitType::Capacity) || (PurchAir.HeatingLimit == LimitType::FlowRateAndCapacity)) {
    2495            0 :                         CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2496            0 :                         HeatSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
    2497            0 :                         if (HeatSensOutput > PurchAir.MaxHeatSensCap) {
    2498            0 :                             PurchAir.SupplyTemp = PurchAir.MaxHeatSensCap / (SupplyMassFlowRate * CpAir) + PurchAir.MixedAirTemp;
    2499            0 :                             HeatSensOutput = PurchAir.MaxHeatSensCap;
    2500              :                         }
    2501              :                     }
    2502              :                 } else { // Heat is off or operating mode is deadband (i.e. don't do any heating)
    2503            1 :                     PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
    2504              :                 }
    2505              : 
    2506              :                 // Set supply humidity ratio first for heating/humidification
    2507          275 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2508          275 :                 switch (PurchAir.HumidCtrlType) {
    2509            0 :                 case HumControl::None: {
    2510            0 :                     PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2511            0 :                 } break;
    2512            0 :                 case HumControl::Humidistat: {
    2513            0 :                     MdotZnHumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToHumidSP;
    2514            0 :                     SupplyHumRatForHumid = MdotZnHumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
    2515            0 :                     SupplyHumRatForHumid = min(SupplyHumRatForHumid, PurchAir.MaxHeatSuppAirHumRat);
    2516            0 :                     PurchAir.SupplyHumRat = max(PurchAir.SupplyHumRat, SupplyHumRatForHumid);
    2517            0 :                 } break;
    2518          275 :                 case HumControl::ConstantSupplyHumidityRatio: {
    2519          275 :                     if (OperatingMode == OpMode::Heat) {
    2520              :                         // If this results in dehumidification, must check cooling capacity limit
    2521          274 :                         if (PurchAir.MixedAirHumRat > PurchAir.MaxHeatSuppAirHumRat) {
    2522          177 :                             if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
    2523            0 :                                 PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
    2524            0 :                                 SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2525            0 :                                 CoolTotOutput = SupplyMassFlowRate * (MixedAirEnthalpy - SupplyEnthalpy);
    2526            0 :                                 CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2527            0 :                                 CoolSensOutput = SupplyMassFlowRate * CpAir * (PurchAir.MixedAirTemp - PurchAir.SupplyTemp);
    2528            0 :                                 CoolLatOutput = CoolTotOutput - CoolSensOutput;
    2529            0 :                                 if (CoolLatOutput >= PurchAir.MaxCoolTotCap) {
    2530            0 :                                     CoolLatOutput = PurchAir.MaxCoolTotCap;
    2531            0 :                                     CoolTotOutput = CoolSensOutput + CoolLatOutput;
    2532            0 :                                     SupplyEnthalpy = MixedAirEnthalpy - CoolTotOutput / SupplyMassFlowRate;
    2533            0 :                                     PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2534              :                                 }
    2535              :                             } else {
    2536          177 :                                 PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
    2537              :                             }
    2538              :                         } else {
    2539           97 :                             PurchAir.SupplyHumRat = PurchAir.MaxHeatSuppAirHumRat;
    2540              :                         }
    2541              :                     } else {
    2542            1 :                         PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2543              :                     }
    2544          275 :                 } break;
    2545            0 :                 default: {
    2546            0 :                     PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2547            0 :                 } break;
    2548              :                 }
    2549              :                 // SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2550              : 
    2551              :                 // Check supply humidity ratio for dehumidification (SupplyHumRatForHumid should always be < SupplyHumRatForDehum)
    2552              :                 // This section is the heating/deadband section, so dehumidification should activate
    2553              :                 //   only if dehumidification control = humidistat
    2554              :                 //   and if humidification control = humidistat or none or if operating in deadband mode
    2555          275 :                 if (CoolOn) {
    2556          275 :                     if (PurchAir.DehumidCtrlType == HumControl::Humidistat) {
    2557            0 :                         if ((PurchAir.HumidCtrlType == HumControl::Humidistat) || (PurchAir.HumidCtrlType == HumControl::None) ||
    2558              :                             (OperatingMode == OpMode::DeadBand)) {
    2559            0 :                             MdotZnDehumidSP = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(ControlledZoneNum).RemainingOutputReqToDehumidSP;
    2560            0 :                             SupplyHumRatForDehum = MdotZnDehumidSP / SupplyMassFlowRate + state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
    2561            0 :                             SupplyHumRatForDehum = max(SupplyHumRatForDehum, PurchAir.MinCoolSuppAirHumRat);
    2562            0 :                             PurchAir.SupplyHumRat = min(PurchAir.SupplyHumRat, SupplyHumRatForDehum);
    2563            0 :                             SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2564            0 :                             if (PurchAir.SupplyHumRat < PurchAir.MixedAirHumRat) {
    2565              :                                 // At this point, the system is heating or deadband but dehumidifying, check max cooling cap limit
    2566            0 :                                 CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2567            0 :                                 SensOutput = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
    2568            0 :                                 LatOutput = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy) - SensOutput;
    2569            0 :                                 if ((PurchAir.CoolingLimit == LimitType::Capacity) || (PurchAir.CoolingLimit == LimitType::FlowRateAndCapacity)) {
    2570            0 :                                     if (LatOutput > PurchAir.MaxCoolTotCap) {
    2571            0 :                                         LatOutput = PurchAir.MaxCoolTotCap;
    2572            0 :                                         SupplyEnthalpy = MixedAirEnthalpy + (LatOutput + SensOutput) / SupplyMassFlowRate;
    2573            0 :                                         PurchAir.SupplyHumRat = PsyWFnTdbH(state, PurchAir.SupplyTemp, SupplyEnthalpy, RoutineName);
    2574              :                                     }
    2575              :                                 }
    2576              :                             }
    2577              :                         }
    2578              :                     }
    2579              :                 }
    2580              : 
    2581              :                 //   Limit supply humidity ratio to saturation at supply outlet temp
    2582              : 
    2583          275 :                 SupplyHumRatOrig = PurchAir.SupplyHumRat;
    2584          275 :                 PurchAir.SupplyHumRat =
    2585          275 :                     min(PurchAir.SupplyHumRat, PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName));
    2586          275 :                 SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2587              : 
    2588              :             } else { // SupplyMassFlowRate is zero
    2589          133 :                 SupplyEnthalpy = MixedAirEnthalpy;
    2590          133 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2591          133 :                 PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
    2592          133 :                 HeatSensOutput = 0.0;
    2593              :             }
    2594              : 
    2595              :         } // Cooling or heating required
    2596              : 
    2597          766 :         if (SupplyMassFlowRate > 0.0) {
    2598              :             // EMS override point  Purch air supply temp and humidity ratio ..... but only if unit is on, SupplyMassFlowRate>0.0
    2599          633 :             if (PurchAir.EMSOverrideSupplyTempOn) {
    2600            1 :                 PurchAir.SupplyTemp = PurchAir.EMSValueSupplyTemp;
    2601              :             }
    2602          633 :             if (PurchAir.EMSOverrideSupplyHumRatOn) {
    2603            1 :                 PurchAir.SupplyHumRat = PurchAir.EMSValueSupplyHumRat;
    2604              :             }
    2605          633 :             SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2606              : 
    2607              :             // compute coil loads
    2608          633 :             if ((PurchAir.SupplyHumRat == PurchAir.MixedAirHumRat) && (PurchAir.SupplyTemp == PurchAir.MixedAirTemp)) {
    2609              :                 // If no change in humrat or temp, then set loads to zero
    2610            0 :                 PurchAir.SenCoilLoad = 0.0;
    2611            0 :                 PurchAir.LatCoilLoad = 0.0;
    2612          633 :             } else if ((PurchAir.SupplyHumRat == PurchAir.MixedAirHumRat) && (PurchAir.SupplyTemp != PurchAir.MixedAirTemp)) {
    2613              :                 // If no change in humrat, then set latent load to zero and use enthalpies to calculate sensible load
    2614            0 :                 PurchAir.SenCoilLoad = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy);
    2615            0 :                 PurchAir.LatCoilLoad = 0.0;
    2616              :             } else {
    2617          633 :                 CpAir = PsyCpAirFnW(PurchAir.MixedAirHumRat);
    2618          633 :                 PurchAir.SenCoilLoad = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - PurchAir.MixedAirTemp);
    2619          633 :                 PurchAir.LatCoilLoad = SupplyMassFlowRate * (SupplyEnthalpy - MixedAirEnthalpy) - PurchAir.SenCoilLoad;
    2620              :             }
    2621              : 
    2622              :             // Apply heating and cooling availability schedules to sensible load
    2623          633 :             if (((PurchAir.SenCoilLoad > 0.0) && !HeatOn) || ((PurchAir.SenCoilLoad < 0.0) && !CoolOn)) {
    2624              :                 // Coil is off
    2625            0 :                 PurchAir.SenCoilLoad = 0.0;
    2626            0 :                 PurchAir.SupplyTemp = PurchAir.MixedAirTemp;
    2627              :             }
    2628              : 
    2629              :             // Apply heating and cooling availability schedules to latent load
    2630          633 :             if (((PurchAir.LatCoilLoad > 0.0) && !HeatOn) || ((PurchAir.LatCoilLoad < 0.0) && !CoolOn)) {
    2631              :                 // Coil is off
    2632            0 :                 PurchAir.LatCoilLoad = 0.0;
    2633            0 :                 PurchAir.SupplyHumRat = PurchAir.MixedAirHumRat;
    2634              :             }
    2635              : 
    2636              :             // Double-check if saturation exceeded, then throw warning, shouldn't happen here, don't reset, just warn
    2637              : 
    2638          633 :             SupplyHumRatOrig = PurchAir.SupplyHumRat;
    2639          633 :             SupplyHumRatSat = PsyWFnTdbRhPb(state, PurchAir.SupplyTemp, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
    2640              : 
    2641          633 :             DeltaHumRat = SupplyHumRatOrig - SupplyHumRatSat;
    2642          633 :             if (DeltaHumRat > SmallDeltaHumRat) {
    2643            0 :                 if (PurchAir.SaturationOutputError < 1) {
    2644            0 :                     ++PurchAir.SaturationOutputError;
    2645            0 :                     ShowWarningError(state,
    2646            0 :                                      format("{} \"{}\" Supply humidity ratio = {:.5T} exceeds saturation limit {:.5T} [kgWater/kgDryAir]",
    2647            0 :                                             PurchAir.cObjectName,
    2648            0 :                                             PurchAir.Name,
    2649              :                                             SupplyHumRatOrig,
    2650              :                                             SupplyHumRatSat));
    2651            0 :                     ShowContinueError(state, " Simulation continuing . . . ");
    2652            0 :                     ShowContinueErrorTimeStamp(state, "");
    2653              :                 } else {
    2654            0 :                     ShowRecurringWarningErrorAtEnd(
    2655              :                         state,
    2656            0 :                         PurchAir.cObjectName + " \"" + PurchAir.Name +
    2657              :                             "\" Supply humidity ratio exceeds saturation limit warning continues, delta max/min [kgWater/kgDryAir]...",
    2658            0 :                         PurchAir.SaturationOutputIndex,
    2659              :                         DeltaHumRat,
    2660              :                         DeltaHumRat);
    2661              :                 }
    2662              :             }
    2663              : 
    2664          633 :             SupplyEnthalpy = PsyHFnTdbW(PurchAir.SupplyTemp, PurchAir.SupplyHumRat);
    2665              : 
    2666          633 :             CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2667          633 :             SysOutputProvided = SupplyMassFlowRate * CpAir * (PurchAir.SupplyTemp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2668          633 :             MoistOutputProvided = SupplyMassFlowRate * (PurchAir.SupplyHumRat - state.dataLoopNodes->Node(ZoneNodeNum).HumRat); // Latent rate, kg/s
    2669              : 
    2670          633 :             PurchAir.SenOutputToZone = SysOutputProvided;
    2671          633 :             PurchAir.LatOutputToZone =
    2672          633 :                 SupplyMassFlowRate * (SupplyEnthalpy - state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy) - PurchAir.SenOutputToZone;
    2673              : 
    2674          633 :             CpAir = PsyCpAirFnW(thisZoneHB.airHumRat);
    2675          633 :             if (PurchAir.OutdoorAir) {
    2676            1 :                 PurchAir.OASenOutput =
    2677            1 :                     OAMassFlowRate * CpAir * (state.dataLoopNodes->Node(OANodeNum).Temp - state.dataLoopNodes->Node(ZoneNodeNum).Temp);
    2678            1 :                 PurchAir.OALatOutput =
    2679            1 :                     OAMassFlowRate * (state.dataLoopNodes->Node(OANodeNum).Enthalpy - state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy) -
    2680            1 :                     PurchAir.OASenOutput;
    2681              :             } else {
    2682          632 :                 PurchAir.OASenOutput = 0.0;
    2683          632 :                 PurchAir.OALatOutput = 0.0;
    2684              :             }
    2685          633 :             if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    2686            0 :                 if (PurchAir.OutdoorAir) {
    2687            0 :                     state.dataLoopNodes->Node(InNodeNum).CO2 = ((SupplyMassFlowRate - OAMassFlowRate) * state.dataLoopNodes->Node(RecircNodeNum).CO2 +
    2688            0 :                                                                 OAMassFlowRate * state.dataLoopNodes->Node(OANodeNum).CO2) /
    2689              :                                                                SupplyMassFlowRate;
    2690              :                 } else {
    2691            0 :                     state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(RecircNodeNum).CO2;
    2692              :                 }
    2693              :             }
    2694          633 :             if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    2695            0 :                 if (PurchAir.OutdoorAir) {
    2696            0 :                     state.dataLoopNodes->Node(InNodeNum).GenContam =
    2697            0 :                         ((SupplyMassFlowRate - OAMassFlowRate) * state.dataLoopNodes->Node(RecircNodeNum).GenContam +
    2698            0 :                          OAMassFlowRate * state.dataLoopNodes->Node(OANodeNum).GenContam) /
    2699              :                         SupplyMassFlowRate;
    2700              :                 } else {
    2701            0 :                     state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(RecircNodeNum).GenContam;
    2702              :                 }
    2703              :             }
    2704              :         } else { // SupplyMassFlowRate = 0.0
    2705          133 :             SysOutputProvided = 0.0;
    2706          133 :             MoistOutputProvided = 0.0;
    2707              : 
    2708          133 :             PurchAir.SenOutputToZone = 0.0;
    2709          133 :             PurchAir.LatOutputToZone = 0.0;
    2710          133 :             PurchAir.SenCoilLoad = 0.0;
    2711          133 :             PurchAir.LatCoilLoad = 0.0;
    2712          133 :             PurchAir.OASenOutput = 0.0;
    2713          133 :             PurchAir.OALatOutput = 0.0;
    2714              : 
    2715          133 :             PurchAir.MixedAirTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
    2716          133 :             PurchAir.MixedAirHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
    2717          133 :             if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    2718              : 
    2719            0 :                 state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(ZoneNodeNum).CO2;
    2720              :             }
    2721          133 :             if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    2722            0 :                 state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(ZoneNodeNum).GenContam;
    2723              :             }
    2724              :         }
    2725              : 
    2726          766 :         state.dataLoopNodes->Node(InNodeNum).Temp = PurchAir.SupplyTemp;
    2727          766 :         state.dataLoopNodes->Node(InNodeNum).HumRat = PurchAir.SupplyHumRat;
    2728          766 :         state.dataLoopNodes->Node(InNodeNum).Enthalpy = SupplyEnthalpy;
    2729          766 :         state.dataLoopNodes->Node(InNodeNum).MassFlowRate = SupplyMassFlowRate;
    2730          766 :         if (PurchAir.OutdoorAir) state.dataLoopNodes->Node(OANodeNum).MassFlowRate = OAMassFlowRate;
    2731              : 
    2732              :     } else { // purchased air OFF
    2733              : 
    2734            0 :         SysOutputProvided = 0.0;
    2735            0 :         MoistOutputProvided = 0.0;
    2736            0 :         SupplyMassFlowRate = 0.0;
    2737            0 :         OAMassFlowRate = 0.0;
    2738            0 :         state.dataLoopNodes->Node(InNodeNum).Temp = state.dataLoopNodes->Node(ZoneNodeNum).Temp;
    2739            0 :         state.dataLoopNodes->Node(InNodeNum).HumRat = state.dataLoopNodes->Node(ZoneNodeNum).HumRat;
    2740            0 :         state.dataLoopNodes->Node(InNodeNum).Enthalpy = state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy;
    2741            0 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    2742            0 :             state.dataLoopNodes->Node(InNodeNum).CO2 = state.dataLoopNodes->Node(ZoneNodeNum).CO2;
    2743              :         }
    2744            0 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    2745            0 :             state.dataLoopNodes->Node(InNodeNum).GenContam = state.dataLoopNodes->Node(ZoneNodeNum).GenContam;
    2746              :         }
    2747              : 
    2748            0 :         state.dataLoopNodes->Node(InNodeNum).MassFlowRate = 0.0;
    2749            0 :         if (PurchAir.OutdoorAir) state.dataLoopNodes->Node(OANodeNum).MassFlowRate = 0.0;
    2750            0 :         PurchAir.SenHeatRate = 0.0;
    2751            0 :         PurchAir.SenCoolRate = 0.0;
    2752            0 :         PurchAir.TotCoolRate = 0.0;
    2753              : 
    2754            0 :         PurchAir.SenOutputToZone = 0.0;
    2755            0 :         PurchAir.LatOutputToZone = 0.0;
    2756            0 :         PurchAir.SenCoilLoad = 0.0;
    2757            0 :         PurchAir.LatCoilLoad = 0.0;
    2758            0 :         PurchAir.OASenOutput = 0.0;
    2759            0 :         PurchAir.OALatOutput = 0.0;
    2760            0 :         PurchAir.MixedAirTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
    2761            0 :         PurchAir.MixedAirHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
    2762            0 :         PurchAir.SupplyTemp = state.dataLoopNodes->Node(InNodeNum).Temp;
    2763            0 :         PurchAir.SupplyHumRat = state.dataLoopNodes->Node(InNodeNum).HumRat;
    2764              :     }
    2765              : 
    2766          766 :     PurchAir.OutdoorAirMassFlowRate = OAMassFlowRate;
    2767          766 :     PurchAir.OutdoorAirVolFlowRateStdRho = OAMassFlowRate / state.dataEnvrn->StdRhoAir;
    2768          766 :     PurchAir.SupplyAirMassFlowRate = SupplyMassFlowRate;
    2769              : 
    2770          766 :     PurchAir.SupplyAirVolFlowRateStdRho = SupplyMassFlowRate / state.dataEnvrn->StdRhoAir;
    2771              : 
    2772          766 :     if (PurchAir.PlenumExhaustAirNodeNum > 0) {
    2773            1 :         state.dataLoopNodes->Node(PurchAir.PlenumExhaustAirNodeNum).MassFlowRate = SupplyMassFlowRate;
    2774              :     }
    2775          766 :     state.dataLoopNodes->Node(RecircNodeNum).MassFlowRate = SupplyMassFlowRate;
    2776          766 : }
    2777              : 
    2778          766 : void CalcPurchAirMinOAMassFlow(EnergyPlusData &state,
    2779              :                                int const PurchAirNum, // index to ideal loads unit
    2780              :                                int const ZoneNum,     // index to zone
    2781              :                                Real64 &OAMassFlowRate // outside air mass flow rate [kg/s] from volume flow using std density
    2782              : )
    2783              : {
    2784              : 
    2785              :     // SUBROUTINE INFORMATION:
    2786              :     //       AUTHOR         M. Witte (GARD)
    2787              :     //       DATE WRITTEN   Jun 2011 (taken from HVACSingleDuctSystem.cc and adapted for Ideal Loads System)
    2788              : 
    2789              :     // PURPOSE OF THIS SUBROUTINE:
    2790              :     // Calculates the amount of outside air required based on optional user input.
    2791              :     // Zone multipliers have been applied in GetInput.
    2792              : 
    2793              :     // METHODOLOGY EMPLOYED:
    2794              :     // User input defines method used to calculate OA.
    2795              : 
    2796              :     // FUNCTION PARAMETER DEFINITIONS:
    2797          766 :     bool constexpr UseMinOASchFlag = true; // Always use min OA schedule in calculations.
    2798              : 
    2799          766 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    2800              : 
    2801          766 :     if (PurchAir.OutdoorAir) {
    2802              :         bool UseOccSchFlag; // TRUE = use actual occupancy, FALSE = use total zone people
    2803              : 
    2804            1 :         if (PurchAir.DCVType == DCV::OccupancySchedule) {
    2805            0 :             UseOccSchFlag = true;
    2806              :         } else {
    2807            1 :             UseOccSchFlag = false;
    2808              :         }
    2809              :         Real64 OAVolumeFlowRate =
    2810            1 :             DataSizing::calcDesignSpecificationOutdoorAir(state, PurchAir.OARequirementsPtr, ZoneNum, UseOccSchFlag, UseMinOASchFlag);
    2811            1 :         OAMassFlowRate = OAVolumeFlowRate * state.dataEnvrn->StdRhoAir;
    2812              : 
    2813              :         // If DCV with CO2SetPoint then check required OA flow to meet CO2 setpoint
    2814            1 :         if (PurchAir.DCVType == DCV::CO2SetPoint) {
    2815            0 :             OAMassFlowRate = max(OAMassFlowRate, state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP);
    2816              :         }
    2817              : 
    2818            1 :         if (OAMassFlowRate <= HVAC::VerySmallMassFlow) OAMassFlowRate = 0.0;
    2819              : 
    2820              :     } else { // No outdoor air
    2821          765 :         OAMassFlowRate = 0.0;
    2822              :     }
    2823          766 :     PurchAir.MinOAMassFlowRate = OAMassFlowRate;
    2824          766 : }
    2825              : 
    2826          767 : void CalcPurchAirMixedAir(EnergyPlusData &state,
    2827              :                           int const PurchAirNum,           // index to ideal loads unit
    2828              :                           Real64 const OAMassFlowRate,     // outside air mass flow rate [kg/s]
    2829              :                           Real64 const SupplyMassFlowRate, // supply air mass flow rate [kg/s]
    2830              :                           Real64 &MixedAirTemp,            // Mixed air dry bulb temperature [C]
    2831              :                           Real64 &MixedAirHumRat,          // Mixed air humidity ratio [kgWater/kgDryAir]
    2832              :                           Real64 &MixedAirEnthalpy,        // Mixed air enthalpy [J/kg]
    2833              :                           OpMode const OperatingMode       // current operating mode, Off, Heating, Cooling, or DeadBand
    2834              : )
    2835              : {
    2836              : 
    2837              :     // SUBROUTINE INFORMATION:
    2838              :     //       AUTHOR         M. Witte (GARD)
    2839              :     //       DATE WRITTEN   Sep 2011
    2840              :     //       MODIFIED       na
    2841              :     //       RE-ENGINEERED  na
    2842              : 
    2843              :     // PURPOSE OF THIS SUBROUTINE:
    2844              :     // Calculates the mixed air conditions, accounting for heat recovery.
    2845              : 
    2846              :     // SUBROUTINE PARAMETER DEFINITIONS:
    2847              :     static constexpr std::string_view RoutineName("CalcPurchAirMixedAir");
    2848              : 
    2849              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2850              :     int RecircNodeNum;           // Zone return air node
    2851              :     int OANodeNum;               // Outdoor air inlet node
    2852              :     Real64 RecircTemp;           // Recirculated air from zone dry bulb temperature [C]
    2853              :     Real64 RecircHumRat;         // Recirculated air from zone humidity ratio [kgWater/kgDryAir]
    2854              :     Real64 RecircEnthalpy;       // Recirculated air from zone enthalpy [J/kg]
    2855              :     Real64 RecircMassFlowRate;   // Recirculated air mass flow rate [kg/s]
    2856              :     Real64 OAInletTemp;          // Outdoor air inlet dry bulb temperature [C]
    2857              :     Real64 OAInletHumRat;        // Outdoor air inlet humidity ratio [kgWater/kgDryAir]
    2858              :     Real64 OAInletEnthalpy;      // Outdoor air inlet enthalpy [J/kg]
    2859              :     Real64 OAAfterHtRecTemp;     // Outdoor air after heat recovery to mixing box dry bulb temperature [C]
    2860              :     Real64 OAAfterHtRecHumRat;   // Outdoor air after heat recovery to mixing box humidity ratio [kgWater/kgDryAir]
    2861              :     Real64 OAAfterHtRecEnthalpy; // Outdoor air after heat recovery to mixing box enthalpy [J/kg]
    2862              :     bool HeatRecOn;
    2863              :     Real64 CpAir; // Specific heat [J/kg-C] reused in multiple places
    2864              : 
    2865          767 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    2866              : 
    2867              :     // Initializations
    2868          767 :     OANodeNum = PurchAir.OutdoorAirNodeNum;
    2869          767 :     RecircNodeNum = PurchAir.ZoneRecircAirNodeNum;
    2870              : 
    2871          767 :     RecircMassFlowRate = 0.0;
    2872          767 :     RecircTemp = state.dataLoopNodes->Node(RecircNodeNum).Temp;
    2873          767 :     RecircHumRat = state.dataLoopNodes->Node(RecircNodeNum).HumRat;
    2874          767 :     RecircEnthalpy = state.dataLoopNodes->Node(RecircNodeNum).Enthalpy;
    2875          767 :     if (PurchAir.OutdoorAir) {
    2876            2 :         OAInletTemp = state.dataLoopNodes->Node(OANodeNum).Temp;
    2877            2 :         OAInletHumRat = state.dataLoopNodes->Node(OANodeNum).HumRat;
    2878            2 :         OAInletEnthalpy = state.dataLoopNodes->Node(OANodeNum).Enthalpy;
    2879            2 :         OAAfterHtRecTemp = OAInletTemp;
    2880            2 :         OAAfterHtRecHumRat = OAInletHumRat;
    2881            2 :         OAAfterHtRecEnthalpy = OAInletEnthalpy;
    2882              :     } else {
    2883          765 :         OAInletTemp = 0.0;
    2884          765 :         OAInletHumRat = 0.0;
    2885          765 :         OAInletEnthalpy = 0.0;
    2886          765 :         OAAfterHtRecTemp = OAInletTemp;
    2887          765 :         OAAfterHtRecHumRat = OAInletHumRat;
    2888          765 :         OAAfterHtRecEnthalpy = OAInletEnthalpy;
    2889              :     }
    2890          767 :     HeatRecOn = false;
    2891              : 
    2892          767 :     if (PurchAir.OutdoorAir && (OAMassFlowRate > 0.0)) {
    2893              :         // Determine if heat recovery is beneficial
    2894            1 :         if (PurchAir.HtRecType == HeatRecovery::Sensible) {
    2895            1 :             if ((OperatingMode == OpMode::Heat) && (RecircTemp > OAInletTemp)) HeatRecOn = true;
    2896            1 :             if ((OperatingMode == OpMode::Cool) && (RecircTemp < OAInletTemp)) HeatRecOn = true;
    2897              :         }
    2898            1 :         if (PurchAir.HtRecType == HeatRecovery::Enthalpy) {
    2899            0 :             if ((OperatingMode == OpMode::Heat) && (RecircEnthalpy > OAInletEnthalpy)) HeatRecOn = true;
    2900            0 :             if ((OperatingMode == OpMode::Cool) && (RecircEnthalpy < OAInletEnthalpy)) HeatRecOn = true;
    2901              :         }
    2902              :         // Calculate heat recovery if active
    2903            1 :         if (HeatRecOn) {
    2904            0 :             PurchAir.TimeHtRecActive = state.dataHVACGlobal->TimeStepSys;
    2905            0 :             OAAfterHtRecTemp = OAInletTemp + PurchAir.HtRecSenEff * (RecircTemp - OAInletTemp);
    2906            0 :             if (PurchAir.HtRecType == HeatRecovery::Enthalpy)
    2907            0 :                 OAAfterHtRecHumRat = OAInletHumRat + PurchAir.HtRecLatEff * (RecircHumRat - OAInletHumRat);
    2908            0 :             OAAfterHtRecEnthalpy = PsyHFnTdbW(OAAfterHtRecTemp, OAAfterHtRecHumRat);
    2909              :             //   Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
    2910            0 :             if (PsyTsatFnHPb(state, OAAfterHtRecEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName) > OAAfterHtRecTemp) {
    2911            0 :                 OAAfterHtRecTemp = PsyTsatFnHPb(state, OAAfterHtRecEnthalpy, state.dataEnvrn->OutBaroPress, RoutineName);
    2912            0 :                 OAAfterHtRecHumRat = PsyWFnTdbH(state, OAAfterHtRecTemp, OAAfterHtRecEnthalpy, RoutineName);
    2913              :             }
    2914              :         }
    2915              : 
    2916            1 :         if (SupplyMassFlowRate > OAMassFlowRate) {
    2917            1 :             RecircMassFlowRate = SupplyMassFlowRate - OAMassFlowRate;
    2918            1 :             MixedAirEnthalpy =
    2919            1 :                 (RecircMassFlowRate * state.dataLoopNodes->Node(RecircNodeNum).Enthalpy + OAMassFlowRate * OAAfterHtRecEnthalpy) / SupplyMassFlowRate;
    2920            1 :             MixedAirHumRat =
    2921            1 :                 (RecircMassFlowRate * state.dataLoopNodes->Node(RecircNodeNum).HumRat + OAMassFlowRate * OAAfterHtRecHumRat) / SupplyMassFlowRate;
    2922              :             // Mixed air temperature is calculated from the mixed air enthalpy and humidity ratio.
    2923            1 :             MixedAirTemp = PsyTdbFnHW(MixedAirEnthalpy, MixedAirHumRat);
    2924              :         } else {
    2925            0 :             RecircMassFlowRate = 0.0;
    2926            0 :             MixedAirEnthalpy = OAAfterHtRecEnthalpy;
    2927            0 :             MixedAirHumRat = OAAfterHtRecHumRat;
    2928            0 :             MixedAirTemp = OAAfterHtRecTemp;
    2929              :         }
    2930              : 
    2931              :         // Calculate OA and heat recovery sensible and latent rates
    2932            1 :         CpAir = PsyCpAirFnW(OAInletHumRat);
    2933            1 :         PurchAir.HtRecSenOutput = OAMassFlowRate * CpAir * (OAAfterHtRecTemp - OAInletTemp);
    2934            1 :         PurchAir.HtRecLatOutput = OAMassFlowRate * (OAAfterHtRecEnthalpy - OAInletEnthalpy) - PurchAir.HtRecSenOutput;
    2935              : 
    2936              :     } else { // No outdoor air
    2937          766 :         RecircMassFlowRate = SupplyMassFlowRate;
    2938          766 :         MixedAirTemp = RecircTemp;
    2939          766 :         MixedAirHumRat = RecircHumRat;
    2940          766 :         MixedAirEnthalpy = RecircEnthalpy;
    2941          766 :         PurchAir.HtRecSenOutput = 0.0;
    2942          766 :         PurchAir.HtRecLatOutput = 0.0;
    2943              :     }
    2944          767 : }
    2945              : 
    2946          761 : void UpdatePurchasedAir(EnergyPlusData &state, int const PurchAirNum, bool const FirstHVACIteration)
    2947              : {
    2948              : 
    2949              :     // SUBROUTINE INFORMATION:
    2950              :     //       AUTHOR         M. J. Witte
    2951              :     //       DATE WRITTEN   Sep 2011
    2952              :     //       MODIFIED       R. Raustad, July 2017, added return plenum
    2953              :     //       RE-ENGINEERED  na
    2954              : 
    2955              :     // PURPOSE OF THIS SUBROUTINE:
    2956              :     // Update node data for Ideal Loads (purchased air) system
    2957              : 
    2958              :     // USE STATEMENTS:
    2959              :     using ZonePlenum::SimAirZonePlenum;
    2960              : 
    2961              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2962              :     // na
    2963              :     bool FirstCall;
    2964              :     bool SupPathInletChanged;
    2965              : 
    2966          761 :     FirstCall = true;            // just used to avoid redundant calculations
    2967          761 :     SupPathInletChanged = false; // don't care if something changes
    2968              : 
    2969          761 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    2970              : 
    2971          761 :     if (PurchAir.ReturnPlenumIndex > 0) {
    2972              : 
    2973              :         // if connected to a return plenum, set the flag that this ideal loads air system was simulated
    2974            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated(PurchAir.PurchAirArrayIndex) = true;
    2975              : 
    2976              :         // if all ideal loads air systems connected to the same plenum have been simulated, simulate the zone air plenum
    2977            1 :         if (all(state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated)) {
    2978            2 :             SimAirZonePlenum(state,
    2979              :                              PurchAir.ReturnPlenumName,
    2980              :                              DataZoneEquipment::AirLoopHVACZone::ReturnPlenum,
    2981            1 :                              PurchAir.ReturnPlenumIndex,
    2982              :                              FirstHVACIteration,
    2983              :                              FirstCall,
    2984              :                              SupPathInletChanged);
    2985              :             // reset this plenums flags for next iteration
    2986            1 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(PurchAir.ReturnPlenumIndex).IsSimulated = false;
    2987              :         }
    2988              :     }
    2989          761 : }
    2990              : 
    2991          761 : void ReportPurchasedAir(EnergyPlusData &state, int const PurchAirNum)
    2992              : {
    2993              : 
    2994              :     // SUBROUTINE INFORMATION:
    2995              :     //       AUTHOR         Russ Taylor
    2996              :     //       DATE WRITTEN   Nov 1997
    2997              :     //       MODIFIED       na
    2998              :     //       RE-ENGINEERED  na
    2999              : 
    3000              :     // PURPOSE OF THIS SUBROUTINE:
    3001              :     // Calculate values of report variables, if necessary.
    3002              : 
    3003              :     // Using/Aliasing
    3004          761 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3005              : 
    3006          761 :     auto &PurchAir = state.dataPurchasedAirMgr->PurchAir(PurchAirNum);
    3007              : 
    3008              :     // Sort out heating and cooling rates
    3009          761 :     PurchAir.SenHeatRate = max(PurchAir.SenCoilLoad, 0.0);
    3010          761 :     PurchAir.SenCoolRate = std::abs(min(PurchAir.SenCoilLoad, 0.0));
    3011          761 :     PurchAir.LatHeatRate = max(PurchAir.LatCoilLoad, 0.0);
    3012          761 :     PurchAir.LatCoolRate = std::abs(min(PurchAir.LatCoilLoad, 0.0));
    3013          761 :     PurchAir.TotHeatRate = PurchAir.SenHeatRate + PurchAir.LatHeatRate;
    3014          761 :     PurchAir.TotCoolRate = PurchAir.SenCoolRate + PurchAir.LatCoolRate;
    3015              : 
    3016          761 :     PurchAir.ZoneSenHeatRate = max(PurchAir.SenOutputToZone, 0.0);
    3017          761 :     PurchAir.ZoneSenCoolRate = std::abs(min(PurchAir.SenOutputToZone, 0.0));
    3018          761 :     PurchAir.ZoneLatHeatRate = max(PurchAir.LatOutputToZone, 0.0);
    3019          761 :     PurchAir.ZoneLatCoolRate = std::abs(min(PurchAir.LatOutputToZone, 0.0));
    3020          761 :     PurchAir.ZoneTotHeatRate = PurchAir.ZoneSenHeatRate + PurchAir.ZoneLatHeatRate;
    3021          761 :     PurchAir.ZoneTotCoolRate = PurchAir.ZoneSenCoolRate + PurchAir.ZoneLatCoolRate;
    3022              : 
    3023              :     // Sort out outdoor air "loads"
    3024              :     // OASenOutput = Outdoor air sensible output relative to zone conditions [W], <0 means OA is cooler than zone air
    3025              :     // OALatOutput  = Outdoor air latent output relative to zone conditions [W], <0 means OA is drier than zone air
    3026          761 :     if (PurchAir.SenCoilLoad > 0.0) { // Heating is active
    3027          274 :         PurchAir.OASenHeatRate = std::abs(min(PurchAir.OASenOutput, 0.0));
    3028              :     } else {
    3029          487 :         PurchAir.OASenHeatRate = 0.0;
    3030              :     }
    3031          761 :     if (PurchAir.SenCoilLoad < 0.0) { // Cooling is active
    3032          357 :         PurchAir.OASenCoolRate = max(PurchAir.OASenOutput, 0.0);
    3033              :     } else {
    3034          404 :         PurchAir.OASenCoolRate = 0.0;
    3035              :     }
    3036          761 :     if (PurchAir.LatCoilLoad > 0.0) { // Humidification is active
    3037          131 :         PurchAir.OALatHeatRate = std::abs(min(PurchAir.OALatOutput, 0.0));
    3038              :     } else {
    3039          630 :         PurchAir.OALatHeatRate = 0.0;
    3040              :     }
    3041          761 :     if (PurchAir.LatCoilLoad < 0.0) { // Dehumidification is active
    3042          500 :         PurchAir.OALatCoolRate = max(PurchAir.OALatOutput, 0.0);
    3043              :     } else {
    3044          261 :         PurchAir.OALatCoolRate = 0.0;
    3045              :     }
    3046              : 
    3047          761 :     PurchAir.OATotHeatRate = PurchAir.OASenHeatRate + PurchAir.OALatHeatRate;
    3048          761 :     PurchAir.OATotCoolRate = PurchAir.OASenCoolRate + PurchAir.OALatCoolRate;
    3049              : 
    3050          761 :     PurchAir.HtRecSenHeatRate = max(PurchAir.HtRecSenOutput, 0.0);
    3051          761 :     PurchAir.HtRecSenCoolRate = std::abs(min(PurchAir.HtRecSenOutput, 0.0));
    3052          761 :     PurchAir.HtRecLatHeatRate = max(PurchAir.HtRecLatOutput, 0.0);
    3053          761 :     PurchAir.HtRecLatCoolRate = std::abs(min(PurchAir.HtRecLatOutput, 0.0));
    3054          761 :     PurchAir.HtRecTotHeatRate = PurchAir.HtRecSenHeatRate + PurchAir.HtRecLatHeatRate;
    3055          761 :     PurchAir.HtRecTotCoolRate = PurchAir.HtRecSenCoolRate + PurchAir.HtRecLatCoolRate;
    3056              : 
    3057          761 :     PurchAir.SenHeatEnergy = PurchAir.SenHeatRate * TimeStepSysSec;
    3058          761 :     PurchAir.SenCoolEnergy = PurchAir.SenCoolRate * TimeStepSysSec;
    3059          761 :     PurchAir.LatHeatEnergy = PurchAir.LatHeatRate * TimeStepSysSec;
    3060          761 :     PurchAir.LatCoolEnergy = PurchAir.LatCoolRate * TimeStepSysSec;
    3061          761 :     PurchAir.TotHeatEnergy = PurchAir.TotHeatRate * TimeStepSysSec;
    3062          761 :     PurchAir.TotCoolEnergy = PurchAir.TotCoolRate * TimeStepSysSec;
    3063              : 
    3064          761 :     PurchAir.ZoneSenHeatEnergy = PurchAir.ZoneSenHeatRate * TimeStepSysSec;
    3065          761 :     PurchAir.ZoneSenCoolEnergy = PurchAir.ZoneSenCoolRate * TimeStepSysSec;
    3066          761 :     PurchAir.ZoneLatHeatEnergy = PurchAir.ZoneLatHeatRate * TimeStepSysSec;
    3067          761 :     PurchAir.ZoneLatCoolEnergy = PurchAir.ZoneLatCoolRate * TimeStepSysSec;
    3068          761 :     PurchAir.ZoneTotHeatEnergy = PurchAir.ZoneTotHeatRate * TimeStepSysSec;
    3069          761 :     PurchAir.ZoneTotCoolEnergy = PurchAir.ZoneTotCoolRate * TimeStepSysSec;
    3070              : 
    3071          761 :     PurchAir.OASenHeatEnergy = PurchAir.OASenHeatRate * TimeStepSysSec;
    3072          761 :     PurchAir.OASenCoolEnergy = PurchAir.OASenCoolRate * TimeStepSysSec;
    3073          761 :     PurchAir.OALatHeatEnergy = PurchAir.OALatHeatRate * TimeStepSysSec;
    3074          761 :     PurchAir.OALatCoolEnergy = PurchAir.OALatCoolRate * TimeStepSysSec;
    3075          761 :     PurchAir.OATotHeatEnergy = PurchAir.OATotHeatRate * TimeStepSysSec;
    3076          761 :     PurchAir.OATotCoolEnergy = PurchAir.OATotCoolRate * TimeStepSysSec;
    3077              : 
    3078          761 :     PurchAir.HtRecSenHeatEnergy = PurchAir.HtRecSenHeatRate * TimeStepSysSec;
    3079          761 :     PurchAir.HtRecSenCoolEnergy = PurchAir.HtRecSenCoolRate * TimeStepSysSec;
    3080          761 :     PurchAir.HtRecLatHeatEnergy = PurchAir.HtRecLatHeatRate * TimeStepSysSec;
    3081          761 :     PurchAir.HtRecLatCoolEnergy = PurchAir.HtRecLatCoolRate * TimeStepSysSec;
    3082          761 :     PurchAir.HtRecTotHeatEnergy = PurchAir.HtRecTotHeatRate * TimeStepSysSec;
    3083          761 :     PurchAir.HtRecTotCoolEnergy = PurchAir.HtRecTotCoolRate * TimeStepSysSec;
    3084          761 : }
    3085              : 
    3086           49 : Real64 GetPurchasedAirOutAirMassFlow(EnergyPlusData &state, int const PurchAirNum)
    3087              : {
    3088              : 
    3089              :     // FUNCTION INFORMATION:
    3090              :     //       AUTHOR         B Griffith
    3091              :     //       DATE WRITTEN   Dec  2006
    3092              :     //       MODIFIED       na
    3093              :     //       RE-ENGINEERED  na
    3094              : 
    3095              :     // PURPOSE OF THIS FUNCTION:
    3096              :     // lookup function for OA inlet mass flow for ventilation rate reporting
    3097              : 
    3098              :     // METHODOLOGY EMPLOYED:
    3099              :     // most analogous functions look up an outside air node but this function
    3100              :     // gets the actual mass flow of outdoor air, following the features of the model
    3101              : 
    3102           49 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3103            0 :         GetPurchasedAir(state);
    3104            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3105              :     }
    3106           49 :     return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).OutdoorAirMassFlowRate;
    3107              : }
    3108              : 
    3109           49 : int GetPurchasedAirZoneInletAirNode(EnergyPlusData &state, int const PurchAirNum)
    3110              : {
    3111              : 
    3112              :     // FUNCTION INFORMATION:
    3113              :     //       AUTHOR         B Griffith
    3114              :     //       DATE WRITTEN   Dec  2006
    3115              :     //       MODIFIED       Adapted for purchased air by M.J. Witte, Oct 2013
    3116              :     //       RE-ENGINEERED  na
    3117              : 
    3118              :     // PURPOSE OF THIS FUNCTION:
    3119              :     // lookup function for zone inlet node for ventilation rate reporting
    3120              : 
    3121           49 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3122            0 :         GetPurchasedAir(state);
    3123            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3124              :     }
    3125              : 
    3126           49 :     int GetPurchasedAirZoneInletAirNode = 0;
    3127           49 :     if (PurchAirNum > 0 && PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir) {
    3128           49 :         GetPurchasedAirZoneInletAirNode = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ZoneSupplyAirNodeNum;
    3129              :     }
    3130              : 
    3131           49 :     return GetPurchasedAirZoneInletAirNode;
    3132              : }
    3133              : 
    3134           49 : int GetPurchasedAirReturnAirNode(EnergyPlusData &state, int const PurchAirNum)
    3135              : {
    3136              : 
    3137              :     // FUNCTION INFORMATION:
    3138              :     //       AUTHOR         B Griffith
    3139              :     //       DATE WRITTEN   Dec  2006
    3140              :     //       MODIFIED       Adapted for purchased air by M.J. Witte, Oct 2013
    3141              :     //       RE-ENGINEERED  na
    3142              : 
    3143              :     // PURPOSE OF THIS FUNCTION:
    3144              :     // lookup function for recirculation air node for ventilation rate reporting
    3145              : 
    3146           49 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3147            0 :         GetPurchasedAir(state);
    3148            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3149              :     }
    3150              : 
    3151           49 :     int GetPurchasedAirReturnAirNode = 0;
    3152           49 :     if (PurchAirNum > 0 && PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir) {
    3153           49 :         GetPurchasedAirReturnAirNode = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ZoneRecircAirNodeNum;
    3154              :     }
    3155              : 
    3156           49 :     return GetPurchasedAirReturnAirNode;
    3157              : }
    3158              : 
    3159            3 : int getPurchasedAirIndex(EnergyPlusData &state, std::string_view PurchAirName)
    3160              : {
    3161            3 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3162            0 :         GetPurchasedAir(state);
    3163            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3164              :     }
    3165              : 
    3166            3 :     for (int PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
    3167            3 :         if (Util::SameString(state.dataPurchasedAirMgr->PurchAir(PurchAirNum).Name, PurchAirName)) {
    3168            3 :             return PurchAirNum;
    3169              :         }
    3170              :     }
    3171              : 
    3172            0 :     return 0;
    3173              : }
    3174              : 
    3175           49 : Real64 GetPurchasedAirMixedAirTemp(EnergyPlusData &state, int const PurchAirNum)
    3176              : {
    3177              : 
    3178              :     // FUNCTION INFORMATION:
    3179              :     //       AUTHOR         B Griffith
    3180              :     //       DATE WRITTEN   Dec  2006
    3181              :     //       MODIFIED       Adapted for purchased air by M.J. Witte, Oct 2013
    3182              :     //       RE-ENGINEERED  na
    3183              : 
    3184              :     // PURPOSE OF THIS FUNCTION:
    3185              :     // lookup function for mixed air Temp for ventilation rate reporting
    3186              : 
    3187              :     // METHODOLOGY EMPLOYED:
    3188              :     // most analogous functions look up an outside air node but this function
    3189              :     // gets the actual mass flow of outdoor air, following the features of the model
    3190              : 
    3191           49 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3192            0 :         GetPurchasedAir(state);
    3193            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3194              :     }
    3195              : 
    3196           49 :     return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).MixedAirTemp;
    3197              : }
    3198              : 
    3199           49 : Real64 GetPurchasedAirMixedAirHumRat(EnergyPlusData &state, int const PurchAirNum)
    3200              : {
    3201              : 
    3202              :     // FUNCTION INFORMATION:
    3203              :     //       AUTHOR         B Griffith
    3204              :     //       DATE WRITTEN   Dec  2006
    3205              :     //       MODIFIED       Adapted for purchased air by M.J. Witte, Oct 2013
    3206              :     //       RE-ENGINEERED  na
    3207              : 
    3208              :     // PURPOSE OF THIS FUNCTION:
    3209              :     // lookup function for mixed air HumRat for ventilation rate reporting
    3210              : 
    3211              :     // METHODOLOGY EMPLOYED:
    3212              :     // most analogous functions look up an outside air node but this function
    3213              :     // gets the actual mass flow of outdoor air, following the features of the model
    3214              : 
    3215           49 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3216            0 :         GetPurchasedAir(state);
    3217            0 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3218              :     }
    3219              : 
    3220           49 :     return state.dataPurchasedAirMgr->PurchAir(PurchAirNum).MixedAirHumRat;
    3221              : }
    3222              : 
    3223            6 : bool CheckPurchasedAirForReturnPlenum(EnergyPlusData &state, int const ReturnPlenumIndex)
    3224              : {
    3225              : 
    3226              :     // FUNCTION INFORMATION:
    3227              :     //       AUTHOR         R Raustad
    3228              :     //       DATE WRITTEN   July  2017
    3229              : 
    3230              :     // PURPOSE OF THIS FUNCTION:
    3231              :     // lookup function to check if return plenum is used
    3232              : 
    3233              :     // Return value
    3234              :     bool CheckPurchasedAirForReturnPlenum;
    3235              : 
    3236              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    3237              :     int PurchAirNum;
    3238              : 
    3239            6 :     if (state.dataPurchasedAirMgr->GetPurchAirInputFlag) {
    3240            3 :         GetPurchasedAir(state);
    3241            3 :         state.dataPurchasedAirMgr->GetPurchAirInputFlag = false;
    3242              :     }
    3243              : 
    3244            6 :     CheckPurchasedAirForReturnPlenum = false;
    3245            6 :     for (PurchAirNum = 1; PurchAirNum <= state.dataPurchasedAirMgr->NumPurchAir; ++PurchAirNum) {
    3246            0 :         if (ReturnPlenumIndex != state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ReturnPlenumIndex) continue;
    3247            0 :         CheckPurchasedAirForReturnPlenum = true;
    3248              :     }
    3249              : 
    3250            6 :     return CheckPurchasedAirForReturnPlenum;
    3251              : }
    3252              : 
    3253            1 : void InitializePlenumArrays(EnergyPlusData &state, int const PurchAirNum)
    3254              : {
    3255              :     // FUNCTION INFORMATION:
    3256              :     //       AUTHOR         R Raustad
    3257              :     //       DATE WRITTEN   July  2017
    3258              : 
    3259              :     // PURPOSE OF THIS FUNCTION:
    3260              :     // to initialize arrays needed to manage ideal load air system used with return plenums
    3261              :     //
    3262              :     // Example:
    3263              :     // NumPlenumArrays = 2 (same as there are two ZoneHVAC:ReturnPlenums objects connected to two or more ideal loads air systems
    3264              :     // In this example ideal loads air system #4 is not connected to a zone return plenum
    3265              :     //
    3266              :     // ZoneHVAC:ReturnPlenum( 1 ) = ReturnPlenum1 is not connected to any ideal loads air systems
    3267              :     // ZoneHVAC:ReturnPlenum( 2 ) = ReturnPlenum2 is connected to PurchAirPlenumArrays( 1 )
    3268              :     // ZoneHVAC:ReturnPlenum( 3 ) = ReturnPlenum3 is connected to PurchAirPlenumArrays( 2 )
    3269              :     //
    3270              :     // PurchAirPlenumArrays( 1 )
    3271              :     //   PurchAirPlenumArrays( 1 ).NumPurchAir = 2, there are 2 ideal loads air systems connected to this plenum
    3272              :     //      PurchAirPlenumArrays( 1 ).PurchAirArray( 1 ) = 1, ideal loads air system #1 is attached to this plenum
    3273              :     //      PurchAirPlenumArrays( 1 ).PurchAirArray( 2 ) = 3, ideal loads air system #3 is attached to this plenum
    3274              :     //      PurchAirPlenumArrays( 1 ).IsSimulated( 1 ) = true, ideal loads air system #1 has been simulated this iteration
    3275              :     //      PurchAirPlenumArrays( 1 ).IsSimulated( 2 ) = false, ideal loads air system #3 has not yet been simulated this iteration
    3276              :     //
    3277              :     //      Ideal loads air systems keep track of which plenum they are connected to
    3278              :     //      PurchAir( 1 ).PlenumArrayIndex = 1
    3279              :     //      PurchAir( 1 ).ReturnPlenumName = ReturnPlenum2;
    3280              :     //      PurchAir( 3 ).PlenumArrayIndex = 1
    3281              :     //      PurchAir( 3 ).ReturnPlenumName = ReturnPlenum2;
    3282              :     //
    3283              :     //      The ideal loads air systems also keep track of which item they are in the int and bool arrays
    3284              :     //      PurchAir( 1 ).PurchAirArrayIndex = 1
    3285              :     //      PurchAir( 3 ).PurchAirArrayIndex = 2
    3286              :     //
    3287              :     // PurchAirPlenumArrays( 2 )
    3288              :     //   PurchAirPlenumArrays( 2 ).NumPurchAir = 3, there are 3 ideal loads air systems connected to this plenum
    3289              :     //      PurchAirPlenumArrays( 2 ).PurchAirArray( 1 ) = 2, ideal loads air system #2 is attached to this plenum
    3290              :     //      PurchAirPlenumArrays( 2 ).PurchAirArray( 2 ) = 5, ideal loads air system #5 is attached to this plenum
    3291              :     //      PurchAirPlenumArrays( 2 ).PurchAirArray( 3 ) = 6, ideal loads air system #6 is attached to this plenum
    3292              :     //      PurchAirPlenumArrays( 2 ).IsSimulated( 1 ) = true, ideal loads air system #4 has been simulated this iteration
    3293              :     //      PurchAirPlenumArrays( 2 ).IsSimulated( 2 ) = false, ideal loads air system #5 has not yet been simulated this iteration
    3294              :     //      PurchAirPlenumArrays( 2 ).IsSimulated( 3 ) = false, ideal loads air system #6 has not yet been simulated this iteration
    3295              :     //
    3296              :     //      Ideal loads air systems keep track of which plenum they are connected to
    3297              :     //      PurchAir( 2 ).PlenumArrayIndex = 2;
    3298              :     //      PurchAir( 2 ).ReturnPlenumName = ReturnPlenum3;
    3299              :     //      PurchAir( 5 ).PlenumArrayIndex = 2;
    3300              :     //      PurchAir( 5 ).ReturnPlenumName = ReturnPlenum3;
    3301              :     //      PurchAir( 6 ).PlenumArrayIndex = 2;
    3302              :     //      PurchAir( 6 ).ReturnPlenumName = ReturnPlenum3;
    3303              :     //
    3304              :     //      The ideal loads air systems also keep track of which item they are in the int and bool arrays
    3305              :     //      PurchAir( 2 ).PurchAirArrayIndex = 1;
    3306              :     //      PurchAir( 5 ).PurchAirArrayIndex = 2;
    3307              :     //      PurchAir( 6 ).PurchAirArrayIndex = 3;
    3308              :     //
    3309              :     //      Given these connections, the data in the IsSimulated array can be set (or checked) according to this syntax:
    3310              :     //
    3311              :     //      Each time an ideal loads air system is simulated the IsSimulated flag is set to true
    3312              :     //      PurchAirPlenumArrays( PurchAir( PurchNum ).PlenumArrayIndex ).IsSimulated( PurchAir( PurchNum ).PurchAirArrayIndex ) = true;
    3313              :     //
    3314              :     //     if all ideal loads air systems connected to the same plenum have been simulated, simulate the zone air return plenum (once per set of
    3315              :     //     ideal loads systems) if ( all( PurchAirPlenumArrays( PurchAir( PurchAirNum ).ReturnPlenumIndex ).IsSimulated ) ) {
    3316              :     //         SimAirZonePlenum( PurchAir( PurchAirNum ).ReturnPlenumName, DataZoneEquipment::ZoneReturnPlenum_Type, PurchAir( PurchAirNum
    3317              :     //         ).ReturnPlenumIndex, FirstHVACIteration, FirstCall, SupPathInletChanged ); reset all IsSimulated flags for next iteration
    3318              :     //         PurchAirPlenumArrays( PurchAir( PurchAirNum ).ReturnPlenumIndex ).IsSimulated = false;
    3319              :     //     }
    3320              : 
    3321              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    3322              :     int ReturnPlenumIndex;        // index to ZoneHVAC:ReturnPlenum object
    3323              :     bool PlenumNotFound;          // logical to determine if same plenum is used by other ideal loads air systems
    3324            1 :     Array1D_int TempPurchArray;   // temporary array used for dynamic allocation
    3325            1 :     Array1D_bool TempIsSimulated; // temporary array used for dynamic allocation
    3326              : 
    3327              :     // index to ZoneHVAC:ReturnPlenum object
    3328            1 :     ReturnPlenumIndex = state.dataPurchasedAirMgr->PurchAir(PurchAirNum).ReturnPlenumIndex;
    3329            1 :     PlenumNotFound = true;
    3330              : 
    3331              :     // if first time through, set up arrays
    3332            1 :     if (!state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocated()) {
    3333              : 
    3334              :         // the ideal loads air system keeps track of which item this system is in a list
    3335            1 :         state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex = 1;
    3336              :         // keep track of how many arrays (i.e., how many different plenums are attached to different ideal loads air systems
    3337            1 :         state.dataPurchasedAirMgr->NumPlenumArrays = 1;
    3338              : 
    3339              :         // allocate new array
    3340            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
    3341              :         // set counter for how many ideal loads air systems are attached to this plenum
    3342            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).NumPurchAir =
    3343              :             1; // keeps track of how many ideal loads air system are connected to this return plenum
    3344              :         // keep track of which plenum this is ( i.e., PurchAirPlenumArrays(1) is ZoneHVAC:ReturnPlenum #4 )
    3345            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).ReturnPlenumIndex =
    3346              :             ReturnPlenumIndex; // stores index of return plenum (e.g., 4 of 5)
    3347              :         // allocate array holding index to one or more ideal loads air systems
    3348            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray.allocate(1);
    3349              :         // allocate boolean to keep track of whether or not this ideal loads air system has been simulated
    3350            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated.allocate(1);
    3351              :         // save the data
    3352            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray(1) = PurchAirNum;
    3353            1 :         state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated(1) = false;
    3354              : 
    3355              :     } else {
    3356              : 
    3357              :         // find the correct index to PurchAirPlenumArrays
    3358            0 :         for (int ReturnPlenumNum = 1; ReturnPlenumNum <= state.dataPurchasedAirMgr->NumPlenumArrays; ++ReturnPlenumNum) {
    3359            0 :             if (ReturnPlenumIndex != state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).ReturnPlenumIndex) continue;
    3360              : 
    3361              :             // allocate temporary arrays and save existing data
    3362            0 :             TempPurchArray.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
    3363            0 :             TempIsSimulated.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
    3364              :             // these are the  member arrays in an existing PurchAirPlenumArrays
    3365            0 :             TempPurchArray = state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).PurchAirArray;
    3366            0 :             TempIsSimulated = state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).IsSimulated;
    3367              : 
    3368              :             // if this array has been used before, we need to increase member array space to save new PurchAir data
    3369            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir += 1;
    3370              :             // save the location of this ideal loads air system in the member arrays
    3371            0 :             state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex =
    3372            0 :                 state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir;
    3373              : 
    3374              :             // allocate more space, this will wipe out data previously stored
    3375            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
    3376            0 :                 .PurchAirArray.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
    3377            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
    3378            0 :                 .IsSimulated.allocate(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir);
    3379              : 
    3380              :             // re-initialize previous data
    3381            0 :             for (int Loop = 1; Loop < state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir; ++Loop) {
    3382            0 :                 state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).PurchAirArray(Loop) = TempPurchArray(Loop);
    3383            0 :                 state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).IsSimulated(Loop) = TempIsSimulated(Loop);
    3384              :             }
    3385              :             // delete temporary array
    3386            0 :             TempPurchArray.deallocate();
    3387            0 :             TempIsSimulated.deallocate();
    3388              : 
    3389              :             // save new data in expanded member array
    3390            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
    3391            0 :                 .PurchAirArray(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir) = PurchAirNum;
    3392            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum)
    3393            0 :                 .IsSimulated(state.dataPurchasedAirMgr->PurchAirPlenumArrays(ReturnPlenumNum).NumPurchAir) = false;
    3394              : 
    3395            0 :             PlenumNotFound = false;
    3396            0 :             break;
    3397              :         }
    3398              : 
    3399            0 :         if (PlenumNotFound) {
    3400              : 
    3401              :             // need to allocate additional space for new plenum array
    3402              :             // keep track of how many arrays (i.e., how many different plenums are attached to different ideal loads air systems)
    3403            0 :             state.dataPurchasedAirMgr->NumPlenumArrays += 1;
    3404              : 
    3405              :             // allocate temporary array and save existing data
    3406            0 :             state.dataPurchasedAirMgr->TempPurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
    3407            0 :             for (int Loop = 1; Loop < state.dataPurchasedAirMgr->NumPlenumArrays; ++Loop) {
    3408            0 :                 state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir =
    3409            0 :                     state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir;
    3410            0 :                 state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).ReturnPlenumIndex =
    3411            0 :                     state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).ReturnPlenumIndex;
    3412            0 :                 state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).PurchAirArray.allocate(
    3413            0 :                     state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir);
    3414            0 :                 state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).IsSimulated.allocate(
    3415            0 :                     state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir);
    3416            0 :                 for (int Loop2 = 1; Loop2 <= state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).NumPurchAir; ++Loop2) {
    3417            0 :                     state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).PurchAirArray(Loop2) =
    3418            0 :                         state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).PurchAirArray(Loop2);
    3419            0 :                     state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).IsSimulated(Loop2) =
    3420            0 :                         state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).IsSimulated(Loop2);
    3421              :                 }
    3422              :             }
    3423              : 
    3424              :             // delete primary array (probably could just re-allocate, but this is only done a few times per simulation)
    3425            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays.deallocate();
    3426              :             // reallocate to new size
    3427            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays.allocate(state.dataPurchasedAirMgr->NumPlenumArrays);
    3428              : 
    3429              :             // allocate member arrays to same size as before
    3430            0 :             for (int Loop = 1; Loop < state.dataPurchasedAirMgr->NumPlenumArrays; ++Loop) {
    3431            0 :                 state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).PurchAirArray.allocate(
    3432            0 :                     state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir);
    3433            0 :                 state.dataPurchasedAirMgr->PurchAirPlenumArrays(Loop).IsSimulated.allocate(
    3434            0 :                     state.dataPurchasedAirMgr->TempPurchAirPlenumArrays(Loop).NumPurchAir);
    3435              :             }
    3436              : 
    3437              :             // save the data
    3438            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays = state.dataPurchasedAirMgr->TempPurchAirPlenumArrays;
    3439              :             // delete temporary data
    3440            0 :             state.dataPurchasedAirMgr->TempPurchAirPlenumArrays.deallocate();
    3441              : 
    3442              :             // save the index to where this ideal loads air system data is stored
    3443            0 :             state.dataPurchasedAirMgr->PurchAir(PurchAirNum).PurchAirArrayIndex = 1;
    3444              :             // save the number of ideal loads air systems stored in these arrays
    3445            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).NumPurchAir = 1;
    3446              :             // save the index the the ZoneHVAC:ReturnPlenum
    3447            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).ReturnPlenumIndex = ReturnPlenumIndex;
    3448              :             // allocate member array and store data
    3449            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray.allocate(1);
    3450            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).PurchAirArray(1) = PurchAirNum;
    3451              :             // allocate member array and store data
    3452            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated.allocate(1);
    3453            0 :             state.dataPurchasedAirMgr->PurchAirPlenumArrays(state.dataPurchasedAirMgr->NumPlenumArrays).IsSimulated(1) = false;
    3454              :         }
    3455              :     }
    3456            1 : }
    3457              : 
    3458              : } // namespace EnergyPlus::PurchasedAirManager
        

Generated by: LCOV version 2.0-1