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

Generated by: LCOV version 2.0-1