LCOV - code coverage report
Current view: top level - EnergyPlus - MixedAir.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 65.4 % 2890 1891
Test Date: 2025-06-02 12:03:30 Functions: 87.5 % 56 49

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : #include <string>
      51              : 
      52              : // ObjexxFCL Headers
      53              : #include <ObjexxFCL/Array.functions.hh>
      54              : #include <ObjexxFCL/Fmath.hh>
      55              : 
      56              : // EnergyPlus Headers
      57              : #include <EnergyPlus/Autosizing/Base.hh>
      58              : #include <EnergyPlus/BranchNodeConnections.hh>
      59              : #include <EnergyPlus/CurveManager.hh>
      60              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      61              : #include <EnergyPlus/DataContaminantBalance.hh>
      62              : #include <EnergyPlus/DataDefineEquip.hh>
      63              : #include <EnergyPlus/DataEnvironment.hh>
      64              : #include <EnergyPlus/DataHVACGlobals.hh>
      65              : #include <EnergyPlus/DataHeatBalance.hh>
      66              : #include <EnergyPlus/DataIPShortCuts.hh>
      67              : #include <EnergyPlus/DataLoopNode.hh>
      68              : #include <EnergyPlus/DataSizing.hh>
      69              : #include <EnergyPlus/DataZoneControls.hh>
      70              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      71              : #include <EnergyPlus/DataZoneEquipment.hh>
      72              : #include <EnergyPlus/DesiccantDehumidifiers.hh>
      73              : #include <EnergyPlus/EMSManager.hh>
      74              : #include <EnergyPlus/EvaporativeCoolers.hh>
      75              : #include <EnergyPlus/Fans.hh>
      76              : #include <EnergyPlus/FaultsManager.hh>
      77              : #include <EnergyPlus/General.hh>
      78              : #include <EnergyPlus/GeneralRoutines.hh>
      79              : #include <EnergyPlus/GlobalNames.hh>
      80              : #include <EnergyPlus/HVACControllers.hh>
      81              : #include <EnergyPlus/HVACDXHeatPumpSystem.hh>
      82              : #include <EnergyPlus/HVACHXAssistedCoolingCoil.hh>
      83              : #include <EnergyPlus/HVACVariableRefrigerantFlow.hh>
      84              : #include <EnergyPlus/HeatRecovery.hh>
      85              : #include <EnergyPlus/HeatingCoils.hh>
      86              : #include <EnergyPlus/Humidifiers.hh>
      87              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      88              : #include <EnergyPlus/MixedAir.hh>
      89              : #include <EnergyPlus/NodeInputManager.hh>
      90              : #include <EnergyPlus/OutAirNodeManager.hh>
      91              : #include <EnergyPlus/OutputProcessor.hh>
      92              : #include <EnergyPlus/OutputReportPredefined.hh>
      93              : #include <EnergyPlus/PhotovoltaicThermalCollectors.hh>
      94              : #include <EnergyPlus/Psychrometrics.hh>
      95              : #include <EnergyPlus/ScheduleManager.hh>
      96              : #include <EnergyPlus/SetPointManager.hh>
      97              : #include <EnergyPlus/SteamCoils.hh>
      98              : #include <EnergyPlus/TranspiredCollector.hh>
      99              : #include <EnergyPlus/UnitarySystem.hh>
     100              : #include <EnergyPlus/UserDefinedComponents.hh>
     101              : #include <EnergyPlus/UtilityRoutines.hh>
     102              : #include <EnergyPlus/WaterCoils.hh>
     103              : 
     104              : namespace EnergyPlus::MixedAir {
     105              : 
     106              : // Module containing the routines dealing with the mixed air portion
     107              : // of the HVAC air loop.
     108              : 
     109              : // MODULE INFORMATION:
     110              : //       AUTHOR         Fred Buhl
     111              : //       DATE WRITTEN   October 1998
     112              : //       MODIFIED       Shirey/Raustad FSEC, June/Aug 2003, Jan 2004
     113              : //                      Lawrie, March 2006 - Module order (per template)
     114              : //                      Craig Wray 22Aug2010 - Added Fan ComponentModel
     115              : //                      Chandan Sharma, FSEC, 25Aug 2011 - Added ProportionalControl
     116              : //                           to enhance CO2 based DCV control
     117              : //                      Feb 2013 Bereket Nigusse, FSEC
     118              : //                        Added DX Coil Model For 100% OA systems
     119              : //       RE-ENGINEERED  na
     120              : 
     121              : // PURPOSE OF THIS MODULE:
     122              : // To encapsulate the data and algorithms required to
     123              : // simulate the mixed air portion of the EPlus air loop.
     124              : 
     125              : // METHODOLOGY EMPLOYED:
     126              : // An algorithmic controller will be employed - there is no attempt to
     127              : // simulate real controllers for the economizer. The mixed air controller
     128              : // will sense various node conditions and set some node flow rates.  Mixed
     129              : // air components will operate with predetermined flow rates.
     130              : 
     131              : // Using/Aliasing
     132              : using namespace DataLoopNode;
     133              : using namespace DataAirLoop;
     134              : using namespace DataEnvironment;
     135              : using namespace DataSizing;
     136              : using namespace FaultsManager;
     137              : 
     138              : constexpr std::array<std::string_view, static_cast<int>(ControllerKind::Num)> ControllerKindNamesUC{"CONTROLLER:WATERCOIL", "CONTROLLER:OUTDOORAIR"};
     139              : 
     140              : constexpr std::array<std::string_view, static_cast<int>(MixedAirControllerType::Num)> MixedAirControllerTypeNames{
     141              :     "Controller:OutdoorAir", "ZoneHVAC:EnergyRecoveryVentilator:Controller"};
     142              : 
     143              : constexpr std::array<std::string_view, static_cast<int>(CMO::Num)> CurrentModuleObjects{"None",
     144              :                                                                                         "AirLoopHVAC:OutdoorAirSystem",
     145              :                                                                                         "AirLoopHVAC:OutdoorAirSystem:EquipmentList",
     146              :                                                                                         "AirLoopHVAC:ControllerList",
     147              :                                                                                         "AvailabilityManagerAssignmentList",
     148              :                                                                                         "Controller:OutdoorAir",
     149              :                                                                                         "ZoneHVAC:EnergyRecoveryVentilator:Controller",
     150              :                                                                                         "Controller:MechanicalVentilation",
     151              :                                                                                         "OutdoorAir:Mixer"};
     152              : 
     153              : constexpr std::array<std::string_view, static_cast<int>(DataSizing::SysOAMethod::Num)> SOAMNamesUC{"ZONESUM",
     154              :                                                                                                    "STANDARD62.1VENTILATIONRATEPROCEDURE",
     155              :                                                                                                    "INDOORAIRQUALITYPROCEDURE",
     156              :                                                                                                    "PROPORTIONALCONTROLBASEDONOCCUPANCYSCHEDULE",
     157              :                                                                                                    "INDOORAIRQUALITYPROCEDUREGENERICCONTAMINANT",
     158              :                                                                                                    "INDOORAIRQUALITYPROCEDURECOMBINED",
     159              :                                                                                                    "PROPORTIONALCONTROLBASEDONDESIGNOCCUPANCY",
     160              :                                                                                                    "PROPORTIONALCONTROLBASEDONDESIGNOARATE",
     161              :                                                                                                    "STANDARD62.1SIMPLIFIEDPROCEDURE",
     162              :                                                                                                    "STANDARD62.1VENTILATIONRATEPROCEDUREWITHLIMIT"};
     163              : 
     164              : constexpr std::array<std::string_view, static_cast<int>(SimAirServingZones::CompType::Num)> CompTypeNamesUC{
     165              :     "OUTDOORAIR:MIXER",
     166              :     "FAN:CONSTANTVOLUME",
     167              :     "FAN:VARIABLEVOLUME",
     168              :     "COIL:COOLING:WATER",
     169              :     "COIL:HEATING:WATER",
     170              :     "COIL:HEATING:STEAM",
     171              :     "COIL:COOLING:WATER:DETAILEDGEOMETRY",
     172              :     "COIL:HEATING:ELECTRIC",
     173              :     "COIL:HEATING:FUEL",
     174              :     "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED",
     175              :     "COIL:HEATING:DESUPERHEATER",
     176              :     "COILSYSTEM:COOLING:DX",
     177              :     "HEATEXCHANGER:AIRTOAIR:FLATPLATE",
     178              :     "DEHUMIDIFIER:DESICCANT:NOFANS",
     179              :     "SOLARCOLLECTOR:UNGLAZEDTRANSPIRED",
     180              :     "EVAPORATIVECOOLER:DIRECT:CELDEKPAD",
     181              :     "AIRLOOPHVAC:UNITARY:FURNACE:HEATONLY",
     182              :     "AIRLOOPHVAC:UNITARY:FURNACE:HEATCOOL",
     183              :     "HUMIDIFIER:STEAM:ELECTRIC",
     184              :     "DUCT",
     185              :     "AIRLOOPHVAC:UNITARYHEATCOOL:VAVCHANGEOVERBYPASS",
     186              :     "AIRLOOPHVAC:UNITARYHEATPUMP:AIRTOAIR:MULTISPEED",
     187              :     "FAN:COMPONENTMODEL",
     188              :     "COILSYSTEM:HEATING:DX",
     189              :     "COIL:USERDEFINED",
     190              :     "FAN:SYSTEMMODEL",
     191              :     "AIRLOOPHVAC:UNITARYSYSTEM",
     192              :     "ZONEHVAC:TERMINALUNIT:VARIABLEREFRIGERANTFLOW",
     193              :     "SOLARCOLLECTOR:FLATPLATE:PHOTOVOLTAICTHERMAL",
     194              :     "COILSYSTEM:COOLING:WATER"};
     195              : 
     196              : static constexpr std::array<std::string_view, static_cast<int>(DataSizing::SysOAMethod::Num)> printSysOAMethod{
     197              :     "ZoneSum,",
     198              :     "Standard62.1VentilationRateProcedure,",
     199              :     "IndoorAirQualityProcedure,",
     200              :     "ProportionalControlBasedOnOccupancySchedule,",
     201              :     "IndoorAirQualityGenericContaminant,",
     202              :     "IndoorAirQualityProcedureCombined,",
     203              :     "ProportionalControlBasedOnDesignOccupancy,",
     204              :     "ProportionalControlBasedOnDesignOARate,",
     205              :     "Standard62.1SimplifiedProcedure,",
     206              :     "Standard62.1VentilationRateProcedureWithLimit,"};
     207              : 
     208            0 : Real64 OAGetFlowRate(EnergyPlusData &state, int OAPtr)
     209              : {
     210            0 :     Real64 FlowRate(0);
     211            0 :     if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers) && (state.dataEnvrn->StdRhoAir != 0)) {
     212            0 :         FlowRate = state.dataMixedAir->OAController(OAPtr).OAMassFlow / state.dataEnvrn->StdRhoAir;
     213              :     }
     214            0 :     return FlowRate;
     215              : }
     216            0 : Real64 OAGetMinFlowRate(EnergyPlusData &state, int OAPtr)
     217              : {
     218            0 :     Real64 MinFlowRate(0);
     219            0 :     if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
     220            0 :         MinFlowRate = state.dataMixedAir->OAController(OAPtr).MinOA;
     221              :     }
     222            0 :     return MinFlowRate;
     223              : }
     224            0 : void OASetDemandManagerVentilationState(EnergyPlusData &state, int OAPtr, bool aState)
     225              : {
     226            0 :     if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
     227            0 :         state.dataMixedAir->OAController(OAPtr).ManageDemand = aState;
     228              :     }
     229            0 : }
     230            0 : void OASetDemandManagerVentilationFlow(EnergyPlusData &state, int OAPtr, Real64 aFlow)
     231              : {
     232            0 :     if ((OAPtr > 0) && (OAPtr <= state.dataMixedAir->NumOAControllers)) {
     233            0 :         state.dataMixedAir->OAController(OAPtr).DemandLimitFlowRate = aFlow * state.dataEnvrn->StdRhoAir;
     234              :     }
     235            0 : }
     236           32 : int GetOAController(EnergyPlusData &state, std::string const &OAName)
     237              : {
     238           32 :     int CurrentOAController(0);
     239           32 :     for (int i = 1; i <= state.dataMixedAir->NumOAControllers; i++) {
     240           32 :         if (OAName == state.dataMixedAir->OAController(i).Name) {
     241           32 :             CurrentOAController = i;
     242           32 :             break;
     243              :         }
     244              :     }
     245           32 :     return CurrentOAController;
     246              : }
     247              : 
     248        92985 : void ManageOutsideAirSystem(EnergyPlusData &state, std::string const &OASysName, bool const FirstHVACIteration, int const AirLoopNum, int &OASysNum)
     249              : {
     250              : 
     251              :     // SUBROUTINE INFORMATION:
     252              :     //       AUTHOR         Fred Buhl
     253              :     //       DATE WRITTEN   Oct 1998
     254              : 
     255              :     // PURPOSE OF THIS SUBROUTINE
     256              :     // Manage the outside air system
     257              : 
     258        92985 :     if (state.dataMixedAir->GetOASysInputFlag) {
     259            1 :         GetOutsideAirSysInputs(state);
     260            1 :         state.dataMixedAir->GetOASysInputFlag = false;
     261              :     }
     262              : 
     263        92985 :     if (OASysNum == 0) {
     264           23 :         OASysNum = Util::FindItemInList(OASysName, state.dataAirLoop->OutsideAirSys);
     265           23 :         if (OASysNum == 0) {
     266            0 :             ShowFatalError(state, format("ManageOutsideAirSystem: AirLoopHVAC:OutdoorAirSystem not found={}", OASysName));
     267              :         }
     268              :     }
     269              : 
     270        92985 :     InitOutsideAirSys(state, OASysNum, AirLoopNum);
     271              : 
     272        92985 :     SimOutsideAirSys(state, OASysNum, FirstHVACIteration, AirLoopNum);
     273        92985 : }
     274              : 
     275        93000 : void SimOASysComponents(EnergyPlusData &state, int const OASysNum, bool const FirstHVACIteration, int const AirLoopNum)
     276              : {
     277        93000 :     auto &CompType = state.dataMixedAir->CompType;
     278        93000 :     auto &CompName = state.dataMixedAir->CompName;
     279        93000 :     bool ReSim(false);
     280        93000 :     bool Sim(true);
     281        93000 :     bool OAHeatCoil(false);
     282        93000 :     bool OACoolCoil(false);
     283        93000 :     bool OAHX(false);
     284              : 
     285       221540 :     for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
     286       128540 :         CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
     287       128540 :         CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
     288       514160 :         SimOAComponent(state,
     289              :                        CompType,
     290              :                        CompName,
     291       128540 :                        state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
     292              :                        FirstHVACIteration,
     293       128540 :                        state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
     294              :                        AirLoopNum,
     295              :                        Sim,
     296              :                        OASysNum,
     297              :                        OAHeatCoil,
     298              :                        OACoolCoil,
     299              :                        OAHX);
     300       128540 :         if (OAHX) {
     301         3551 :             ReSim = true;
     302              :         }
     303              :     }
     304              :     // if there were heat exchangers and/or desiccant wheel in the OA path, need to simulate again in reverse
     305              :     // order to propagate the air flow and conditions out the relief air path to the relief air exit node
     306        93000 :     if (ReSim) {
     307         7105 :         for (int CompNum = state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents - 1; CompNum >= 1; --CompNum) {
     308         3557 :             CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
     309         3557 :             CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
     310        14228 :             SimOAComponent(state,
     311              :                            CompType,
     312              :                            CompName,
     313         3557 :                            state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
     314              :                            FirstHVACIteration,
     315         3557 :                            state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
     316              :                            AirLoopNum,
     317              :                            Sim,
     318              :                            OASysNum,
     319              :                            OAHeatCoil,
     320              :                            OACoolCoil,
     321              :                            OAHX);
     322              :         }
     323              :         // now simulate again propagate current temps back through OA system
     324        10653 :         for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
     325         7105 :             CompType = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(CompNum);
     326         7105 :             CompName = state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum);
     327        28420 :             SimOAComponent(state,
     328              :                            CompType,
     329              :                            CompName,
     330         7105 :                            state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum),
     331              :                            FirstHVACIteration,
     332         7105 :                            state.dataAirLoop->OutsideAirSys(OASysNum).ComponentIndex(CompNum),
     333              :                            AirLoopNum,
     334              :                            Sim,
     335              :                            OASysNum,
     336              :                            OAHeatCoil,
     337              :                            OACoolCoil,
     338              :                            OAHX);
     339              :         }
     340              :     }
     341        93000 : }
     342              : 
     343        92985 : void SimOutsideAirSys(EnergyPlusData &state, int const OASysNum, bool const FirstHVACIteration, int const AirLoopNum)
     344              : {
     345              : 
     346              :     // SUBROUTINE INFORMATION:
     347              :     //       AUTHOR         Fred Buhl
     348              :     //       DATE WRITTEN   Oct 1998
     349              : 
     350              :     // PURPOSE OF THIS SUBROUTINE
     351              :     // Simulate the controllers and components in the outside air system.
     352              : 
     353              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     354        92985 :     state.dataSize->CurOASysNum = OASysNum;
     355        92985 :     auto &CurrentOASystem(state.dataAirLoop->OutsideAirSys(OASysNum));
     356        92985 :     if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum == -1) {
     357        78417 :         SimOAController(state, CurrentOASystem.OAControllerName, CurrentOASystem.OAControllerIndex, FirstHVACIteration, AirLoopNum);
     358              :     }
     359        92985 :     SimOASysComponents(state, OASysNum, FirstHVACIteration, AirLoopNum);
     360              : 
     361        92985 :     if (state.dataMixedAir->MyOneTimeErrorFlag(OASysNum)) {
     362           29 :         bool FatalErrorFlag(false);
     363           29 :         if (CurrentOASystem.NumControllers - CurrentOASystem.NumSimpleControllers > 1) {
     364            0 :             ShowWarningError(
     365              :                 state,
     366            0 :                 format("AirLoopHVAC:OutdoorAirSystem {} has more than 1 outside air controller; only the 1st will be used", CurrentOASystem.Name));
     367              :         }
     368           70 :         for (int CompNum = 1; CompNum <= CurrentOASystem.NumComponents; ++CompNum) {
     369           41 :             auto &CompType = CurrentOASystem.ComponentType(CompNum);
     370           41 :             auto &CompName = CurrentOASystem.ComponentName(CompNum);
     371           41 :             if (Util::SameString(CompType, "OutdoorAir:Mixer")) {
     372           25 :                 int OAMixerNum = Util::FindItemInList(CompName, state.dataMixedAir->OAMixer);
     373           25 :                 int OAControllerNum = CurrentOASystem.OAControllerIndex;
     374           25 :                 if (state.dataMixedAir->OAController(OAControllerNum).MixNode != state.dataMixedAir->OAMixer(OAMixerNum).MixNode) {
     375            0 :                     ShowSevereError(
     376            0 :                         state, format("The mixed air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
     377            0 :                     ShowContinueError(state,
     378            0 :                                       format("should be the same node as the mixed air node of OutdoorAir:Mixer=\"{}\".",
     379            0 :                                              state.dataMixedAir->OAMixer(OAMixerNum).Name));
     380            0 :                     ShowContinueError(state,
     381            0 :                                       format("Controller:OutdoorAir mixed air node=\"{}\".",
     382            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).MixNode)));
     383            0 :                     ShowContinueError(state,
     384            0 :                                       format("OutdoorAir:Mixer mixed air node=\"{}\".",
     385            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).MixNode)));
     386            0 :                     FatalErrorFlag = true;
     387              :                 }
     388           25 :                 if (state.dataMixedAir->OAController(OAControllerNum).RelNode != state.dataMixedAir->OAMixer(OAMixerNum).RelNode) {
     389            0 :                     ShowSevereError(
     390            0 :                         state, format("The relief air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
     391            0 :                     ShowContinueError(state,
     392            0 :                                       format("should be the same node as the relief air node of OutdoorAir:Mixer=\"{}\".",
     393            0 :                                              state.dataMixedAir->OAMixer(OAMixerNum).Name));
     394            0 :                     ShowContinueError(state,
     395            0 :                                       format("Controller:OutdoorAir relief air node=\"{}\".",
     396            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).RelNode)));
     397            0 :                     ShowContinueError(state,
     398            0 :                                       format("OutdoorAir:Mixer relief air node=\"{}\".",
     399            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).RelNode)));
     400            0 :                     FatalErrorFlag = true;
     401              :                 }
     402           25 :                 if (state.dataMixedAir->OAController(OAControllerNum).RetNode != state.dataMixedAir->OAMixer(OAMixerNum).RetNode) {
     403            0 :                     ShowSevereError(
     404            0 :                         state, format("The return air node of Controller:OutdoorAir=\"{}\"", state.dataMixedAir->OAController(OAControllerNum).Name));
     405            0 :                     ShowContinueError(state,
     406            0 :                                       format("should be the same node as the return air node of OutdoorAir:Mixer=\"{}\".",
     407            0 :                                              state.dataMixedAir->OAMixer(OAMixerNum).Name));
     408            0 :                     ShowContinueError(state,
     409            0 :                                       format("Controller:OutdoorAir return air node=\"{}\".",
     410            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAController(OAControllerNum).RetNode)));
     411            0 :                     ShowContinueError(state,
     412            0 :                                       format("OutdoorAir:Mixer return air node=\"{}\".",
     413            0 :                                              state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OAMixerNum).RetNode)));
     414            0 :                     FatalErrorFlag = true;
     415              :                 }
     416              :             }
     417              :         }
     418           29 :         state.dataMixedAir->MyOneTimeErrorFlag(OASysNum) = false;
     419           29 :         if (FatalErrorFlag) {
     420            0 :             ShowFatalError(state, "Previous severe error(s) cause program termination");
     421              :         }
     422              :     }
     423              : 
     424        92985 :     state.dataSize->CurOASysNum = 0;
     425        92985 :     if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum == -1) {
     426        78417 :         state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysComponentsSimulated = true;
     427              :     }
     428        92985 : }
     429              : 
     430       139316 : void SimOAComponent(EnergyPlusData &state,
     431              :                     std::string const &CompType,                    // the component type
     432              :                     std::string const &CompName,                    // the component Name
     433              :                     SimAirServingZones::CompType const CompTypeNum, // Component Type -- Integerized for this module
     434              :                     bool const FirstHVACIteration,
     435              :                     int &CompIndex,
     436              :                     int const AirLoopNum, // air loop index for economizer lockout coordination
     437              :                     bool const Sim,       // if TRUE, simulate component; if FALSE, just set the coil existence flags
     438              :                     int const OASysNum,   // index to outside air system
     439              :                     bool &OAHeatingCoil,  // TRUE indicates a heating coil has been found
     440              :                     bool &OACoolingCoil,  // TRUE indicates a cooling coil has been found
     441              :                     bool &OAHX)           // TRUE indicates a heat exchanger has been found
     442              : {
     443              : 
     444              :     // SUBROUTINE INFORMATION
     445              :     //             AUTHOR:  Russ Taylor, Dan Fisher, Fred Buhl
     446              :     //       DATE WRITTEN:  Oct 1997
     447              :     //           MODIFIED:  Dec 1997 Fred Buhl, D Shirey Feb/Sept 2003
     448              :     //                      Nov 2004 M. J. Witte, GARD Analytics, Inc.
     449              :     //                        Add DXSystem:AirLoop as valid OA system equipment
     450              :     //                        Work supported by ASHRAE research project 1254-RP
     451              : 
     452              :     // PURPOSE OF THIS SUBROUTINE:
     453              :     // Calls the individual air loop component simulation routines
     454              : 
     455              :     // SUBROUTINE LOCAL VARIABLE DEFINITIONS
     456       139316 :     OAHeatingCoil = false;
     457       139316 :     OACoolingCoil = false;
     458       139316 :     OAHX = false;
     459              :     HVAC::FanOp fanOp;
     460       139316 :     Real64 sensOut = 0.0;
     461       139316 :     int constexpr zoneOAUnitNum = -1;
     462       139316 :     Real64 constexpr OAUCoilOutTemp = 0.0;
     463       139316 :     bool constexpr ZoneEquipFlag = false;
     464       139316 :     bool HeatingActive = false; // why isn't this returning that a coil is active?
     465       139316 :     bool CoolingActive = false;
     466              : 
     467       139316 :     switch (CompTypeNum) {
     468        82073 :     case SimAirServingZones::CompType::OAMixer_Num: { // OutdoorAir:Mixer
     469        82073 :         if (Sim) {
     470        81977 :             SimOAMixer(state, CompName, CompIndex);
     471              :         }
     472        82073 :         break;
     473              :     }
     474        14565 :     case SimAirServingZones::CompType::Fan_Simple_CV:        // Fan:ConstantVolume
     475              :     case SimAirServingZones::CompType::Fan_Simple_VAV:       // Fan:VariableVolume
     476              :     case SimAirServingZones::CompType::Fan_System_Object:    // Fan:SystemModel
     477              :     case SimAirServingZones::CompType::Fan_ComponentModel: { // Fan:ComponentModel
     478        14565 :         if (Sim) {
     479        14565 :             if (CompIndex == 0) { // TODO: get rid of this stuff
     480            0 :                 CompIndex = Fans::GetFanIndex(state, CompName);
     481            0 :                 assert(CompIndex > 0);
     482              :             }
     483              : 
     484        14565 :             state.dataFans->fans(CompIndex)->simulate(state, FirstHVACIteration);
     485              :         }
     486        14565 :     } break;
     487            2 :     case SimAirServingZones::CompType::WaterCoil_Cooling: { // Coil:Cooling:Water
     488            2 :         if (Sim) {
     489              :             // get water coil and controller data if not called previously
     490            0 :             if (CompIndex == 0) {
     491            0 :                 WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
     492              :             }
     493              :             // iterate on OA sys controller and water coil at the same time
     494            0 :             if (!state.dataWaterCoils->WaterCoil(CompIndex).heatRecoveryCoil) {
     495            0 :                 SimAirServingZones::SolveWaterCoilController(state,
     496              :                                                              FirstHVACIteration,
     497              :                                                              AirLoopNum,
     498              :                                                              CompName,
     499              :                                                              CompIndex,
     500            0 :                                                              state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
     501            0 :                                                              state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
     502              :                                                              false);
     503              :                 // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
     504            0 :                 state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
     505              :             } else {
     506            0 :                 WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
     507              :             }
     508              :         } else {
     509              :             // This is not working as intended ... don't want to include the HR coil in sizing.
     510              :             // But if the water coil is called to get this index, then the controller is called to set the
     511              :             // controller index and the simulation sizes the controller before the cooling coil.
     512              :             // Pushing this aspect forward to a follow up issue where the
     513              :             // controller index call is moved out of water coils getInput.
     514              :             // if (CompIndex == 0) {
     515              :             //    bool errFound = false;
     516              :             //    CompIndex = WaterCoils::GetWaterCoilIndex(state, CompType, CompName, errFound);
     517              :             //    if (errFound) ShowFatalError(state, "SimOAComponent: Program terminates for preceding reason.");
     518              :             // }
     519              :             // if (!state.dataWaterCoils->WaterCoil(CompIndex).heatRecoveryCoil) OACoolingCoil = true;
     520              :             // should not include heat recovery coils in sizing since heat transfer at peak cooling is minimal.
     521            2 :             OACoolingCoil = true;
     522              :         }
     523            2 :     } break;
     524         2848 :     case SimAirServingZones::CompType::WaterCoil_SimpleHeat: { // Coil:Heating:Water
     525         2848 :         if (Sim) {
     526              :             // get water coil and controller data if not called previously
     527         2842 :             if (CompIndex == 0) {
     528            2 :                 WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
     529              :             }
     530              :             // iterate on OA sys controller and water coil at the same time
     531         5684 :             SimAirServingZones::SolveWaterCoilController(state,
     532              :                                                          FirstHVACIteration,
     533              :                                                          AirLoopNum,
     534              :                                                          CompName,
     535              :                                                          CompIndex,
     536         2842 :                                                          state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
     537         2842 :                                                          state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
     538              :                                                          false);
     539              :             // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
     540         2842 :             state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
     541              :         }
     542         2848 :         OAHeatingCoil = true;
     543         2848 :     } break;
     544            0 :     case SimAirServingZones::CompType::SteamCoil_AirHeat: { // Coil:Heating:Steam
     545            0 :         if (Sim) {
     546            0 :             SteamCoils::SimulateSteamCoilComponents(state, CompName, FirstHVACIteration, CompIndex, 0.0);
     547              :         }
     548            0 :         OAHeatingCoil = true;
     549            0 :     } break;
     550            0 :     case SimAirServingZones::CompType::WaterCoil_DetailedCool: { // Coil:Cooling:Water:DetailedGeometry
     551            0 :         if (Sim) {
     552              :             // get water coil and controller data if not called previously
     553            0 :             if (CompIndex == 0) {
     554            0 :                 WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompIndex);
     555              :             }
     556              :             // iterate on OA sys controller and water coil at the same time
     557            0 :             SimAirServingZones::SolveWaterCoilController(state,
     558              :                                                          FirstHVACIteration,
     559              :                                                          AirLoopNum,
     560              :                                                          CompName,
     561              :                                                          CompIndex,
     562            0 :                                                          state.dataWaterCoils->WaterCoil(CompIndex).ControllerName,
     563            0 :                                                          state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex,
     564              :                                                          false);
     565              :             // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
     566            0 :             state.dataHVACControllers->ControllerProps(state.dataWaterCoils->WaterCoil(CompIndex).ControllerIndex).BypassControllerCalc = true;
     567              :         }
     568            0 :         OACoolingCoil = true;
     569            0 :     } break;
     570        14595 :     case SimAirServingZones::CompType::Coil_ElectricHeat: // Coil:Heating:Electric
     571              :     case SimAirServingZones::CompType::Coil_GasHeat: {    // Coil:Heating:Fuel
     572        14595 :         if (Sim) {
     573              :             //     stand-alone coils are temperature controlled (do not pass QCoilReq in argument list, QCoilReq overrides temp SP)
     574        14595 :             HeatingCoils::SimulateHeatingCoilComponents(state, CompName, FirstHVACIteration, _, CompIndex);
     575              :         }
     576        14595 :         OAHeatingCoil = true;
     577        14595 :     } break;
     578            0 :     case SimAirServingZones::CompType::WaterCoil_CoolingHXAsst: { // CoilSystem:Cooling:Water:HeatExchangerAssisted
     579            0 :         if (Sim) {
     580              :             // get water coil and controller data if not called previously
     581            0 :             if (CompIndex == 0) {
     582            0 :                 HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
     583              :                     state, CompName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, CompIndex, HVAC::FanOp::Continuous);
     584              :             }
     585              :             // iterate on OA sys controller and water coil at the same time
     586            0 :             SimAirServingZones::SolveWaterCoilController(state,
     587              :                                                          FirstHVACIteration,
     588              :                                                          AirLoopNum,
     589              :                                                          CompName,
     590              :                                                          CompIndex,
     591            0 :                                                          state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerName,
     592            0 :                                                          state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerIndex,
     593              :                                                          true);
     594              :             // set flag to tell HVAC controller it will be simulated only in SolveWaterCoilController()
     595            0 :             state.dataHVACControllers->ControllerProps(state.dataHVACAssistedCC->HXAssistedCoil(CompIndex).ControllerIndex).BypassControllerCalc =
     596              :                 true;
     597              :         }
     598            0 :         OACoolingCoil = true;
     599            0 :     } break;
     600        14571 :     case SimAirServingZones::CompType::DXSystem:             // CoilSystem:Cooling:DX
     601              :     case SimAirServingZones::CompType::CoilSystemWater:      // CoilSystem:Cooling:Water
     602              :     case SimAirServingZones::CompType::UnitarySystemModel: { // AirloopHVAC:UnitarySystem
     603        14571 :         if (Sim) {
     604        14567 :             Real64 latOut = 0.0;     // does the air loop not need to know what the latent capacity is?
     605        14567 :             int compNum = CompIndex; // use local so return value of compNum from simulate call does not overwrite CompIndex
     606        14567 :             state.dataAirLoop->OutsideAirSys(OASysNum).compPointer[compNum]->simulate(state,
     607              :                                                                                       CompName,
     608              :                                                                                       FirstHVACIteration,
     609              :                                                                                       AirLoopNum,
     610              :                                                                                       compNum,
     611              :                                                                                       HeatingActive,
     612              :                                                                                       CoolingActive,
     613              :                                                                                       zoneOAUnitNum,
     614              :                                                                                       OAUCoilOutTemp,
     615              :                                                                                       ZoneEquipFlag,
     616              :                                                                                       sensOut,
     617              :                                                                                       latOut);
     618              :         }
     619        14571 :         if (state.dataMixedAir->MyOneTimeCheckUnitarySysFlag(OASysNum) && CompTypeNum == SimAirServingZones::CompType::UnitarySystemModel) {
     620            0 :             UnitarySystems::UnitarySys::getUnitarySysHeatCoolCoil(state, CompName, OACoolingCoil, OAHeatingCoil, 0);
     621            0 :             UnitarySystems::UnitarySys::checkUnitarySysCoilInOASysExists(state, CompName, 0);
     622            0 :             if (Sim) {
     623            0 :                 state.dataMixedAir->MyOneTimeCheckUnitarySysFlag(OASysNum) = false;
     624              :             }
     625              :         } else {
     626        14571 :             OACoolingCoil = true;
     627              :         }
     628        14571 :     } break;
     629            0 :     case SimAirServingZones::CompType::DXHeatPumpSystem: { // CoilSystem:IntegratedHeatPump:AirSource
     630            0 :         if (Sim) {
     631            0 :             HVACDXHeatPumpSystem::SimDXHeatPumpSystem(state, CompName, FirstHVACIteration, AirLoopNum, CompIndex);
     632              :         }
     633            0 :         OAHeatingCoil = true;
     634            0 :     } break;
     635            0 :     case SimAirServingZones::CompType::CoilUserDefined: { // Coil:UserDefined
     636            0 :         if (Sim) {
     637            0 :             UserDefinedComponents::SimCoilUserDefined(state, CompName, CompIndex, AirLoopNum, OAHeatingCoil, OACoolingCoil);
     638              :         }
     639            0 :     } break;
     640        10657 :     case SimAirServingZones::CompType::HeatXchngr: {
     641              :         // HeatExchanger:AirToAir:FlatPlate, HeatExchanger:AirToAir:SensibleAndLatent, HeatExchanger:Desiccant:BalancedFlow
     642        10657 :         if (Sim) {
     643        10653 :             Real64 AirloopPLR = 1;
     644        10653 :             if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
     645            9 :                 fanOp = HVAC::FanOp::Continuous;
     646              :             } else {
     647        10644 :                 if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).fanOp == HVAC::FanOp::Cycling) {
     648            0 :                     fanOp = HVAC::FanOp::Cycling;
     649              :                 } else {
     650        10644 :                     fanOp = HVAC::FanOp::Continuous;
     651              :                 }
     652              : 
     653        10644 :                 if (fanOp == HVAC::FanOp::Cycling) {
     654              :                     // HX's in the OA system can be troublesome given that the OA flow rate is not necessarily proportional to air loop PLR
     655              :                     // adding that user input for branch flow rate, HX nominal flow rate, OA system min/max flow rate will not necessarily be
     656              :                     // perfectly input, a compromise is used for OA sys HX's as the ratio of flow to max. Issue #4298.
     657              :                     //                    AirloopPLR = AirLoopFlow( AirLoopNum ).FanPLR;
     658            0 :                     AirloopPLR = state.dataMixedAir->OAController(OASysNum).OAMassFlow / state.dataMixedAir->OAController(OASysNum).MaxOAMassFlowRate;
     659              :                 }
     660              :             }
     661        10653 :             if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
     662            9 :                 HeatRecovery::SimHeatRecovery(state, CompName, FirstHVACIteration, CompIndex, fanOp, AirloopPLR, _, _, _, _, _);
     663              :             } else {
     664        21288 :                 HeatRecovery::SimHeatRecovery(state,
     665              :                                               CompName,
     666              :                                               FirstHVACIteration,
     667              :                                               CompIndex,
     668              :                                               fanOp,
     669              :                                               AirloopPLR,
     670              :                                               _,
     671              :                                               _,
     672              :                                               _,
     673        10644 :                                               state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass,
     674        10644 :                                               state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HighHumCtrlActive);
     675              :             }
     676              :         }
     677        10657 :         OAHX = true;
     678        10657 :     } break;
     679            2 :     case SimAirServingZones::CompType::Desiccant: { // Dehumidifier:Desiccant:NoFans,  Dehumidifier:Desiccant:NoFans, Dehumidifier:Desiccant:System
     680            2 :         if (Sim) {
     681            0 :             DesiccantDehumidifiers::SimDesiccantDehumidifier(state, CompName, FirstHVACIteration, CompIndex);
     682              :         }
     683            2 :         OAHX = true;
     684            2 :     } break;
     685            3 :     case SimAirServingZones::CompType::Humidifier: { // Humidifier:Steam:Electric Humidifier:Steam:Gas
     686            3 :         if (Sim) {
     687            3 :             Humidifiers::SimHumidifier(state, CompName, FirstHVACIteration, CompIndex);
     688              :         }
     689            3 :     } break;
     690            0 :     case SimAirServingZones::CompType::Unglazed_SolarCollector: { // SolarCollector:UnglazedTranspired
     691            0 :         if (Sim) {
     692            0 :             TranspiredCollector::SimTranspiredCollector(state, CompName, CompIndex);
     693              :         }
     694            0 :     } break;
     695            0 :     case SimAirServingZones::CompType::PVT_AirBased: { // SolarCollector:FlatPlate:PhotovoltaicThermal
     696            0 :         if (Sim) {
     697            0 :             if (CompIndex == 0) {
     698            0 :                 CompIndex = PhotovoltaicThermalCollectors::getPVTindexFromName(state, CompName);
     699              :             }
     700            0 :             PhotovoltaicThermalCollectors::simPVTfromOASys(state, CompIndex, FirstHVACIteration);
     701              :         }
     702            0 :     } break;
     703            0 :     case SimAirServingZones::CompType::EvapCooler: { // EvaporativeCooler:Direct:CelDekPad, EvaporativeCooler:Indirect:CelDekPad
     704              :         // EvaporativeCooler:Indirect:WetCoil, EvaporativeCooler:Indirect:ResearchSpecial
     705            0 :         if (Sim) {
     706            0 :             EvaporativeCoolers::SimEvapCooler(state, CompName, CompIndex);
     707              :         }
     708            0 :     } break;
     709            0 :     case SimAirServingZones::CompType::ZoneVRFasAirLoopEquip: { // ZoneHVAC:TerminalUnit:VariableRefrigerantFlow
     710            0 :         if (Sim) {
     711            0 :             int ControlledZoneNum = 0;
     712            0 :             int constexpr OAUnitNum = 0;
     713            0 :             Real64 constexpr OAUCoilOutTemp = 0.0;
     714            0 :             bool constexpr ZoneEquipment = false;
     715            0 :             Real64 sysOut = 0.0;
     716            0 :             Real64 latOut = 0.0;
     717            0 :             HVACVariableRefrigerantFlow::SimulateVRF(state,
     718              :                                                      CompName,
     719              :                                                      FirstHVACIteration,
     720              :                                                      ControlledZoneNum,
     721              :                                                      CompIndex,
     722              :                                                      HeatingActive,
     723              :                                                      CoolingActive,
     724              :                                                      OAUnitNum,
     725              :                                                      OAUCoilOutTemp,
     726              :                                                      ZoneEquipment,
     727              :                                                      sysOut,
     728              :                                                      latOut);
     729              :         } else {
     730            0 :             HVACVariableRefrigerantFlow::isVRFCoilPresent(state, CompName, OACoolingCoil, OAHeatingCoil);
     731              :         }
     732            0 :     } break;
     733            0 :     default:
     734            0 :         ShowFatalError(state, format("Invalid Outside Air Component={}", CompType));
     735              :     }
     736       139316 : }
     737              : 
     738       383145 : void SimOAMixer(EnergyPlusData &state, std::string const &CompName, int &CompIndex)
     739              : {
     740              : 
     741              :     // SUBROUTINE INFORMATION:
     742              :     //       AUTHOR         Fred Buhl
     743              :     //       DATE WRITTEN   Oct 1998
     744              : 
     745              :     // PURPOSE OF THIS SUBROUTINE
     746              :     // Simulate an Outside Air Mixer component
     747              : 
     748              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     749              :     int OAMixerNum;
     750              : 
     751       383145 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
     752            5 :         GetOAMixerInputs(state);
     753            5 :         state.dataMixedAir->GetOAMixerInputFlag = false;
     754              :     }
     755              : 
     756       383145 :     if (CompIndex == 0) {
     757           73 :         OAMixerNum = Util::FindItemInList(CompName, state.dataMixedAir->OAMixer);
     758           73 :         CompIndex = OAMixerNum;
     759           73 :         if (OAMixerNum == 0) {
     760            0 :             ShowFatalError(state, format("SimOAMixer: OutdoorAir:Mixer not found={}", CompName));
     761              :         }
     762              :     } else {
     763       383072 :         OAMixerNum = CompIndex;
     764              :     }
     765              : 
     766       383145 :     auto &mixer = state.dataMixedAir->OAMixer(OAMixerNum);
     767              : 
     768       383145 :     mixer.InitOAMixer(state);
     769              : 
     770       383145 :     mixer.CalcOAMixer(state);
     771              : 
     772       383145 :     mixer.UpdateOAMixer(state);
     773       383145 : }
     774              : 
     775        78418 : void SimOAController(EnergyPlusData &state, std::string const &CtrlName, int &CtrlIndex, bool const FirstHVACIteration, int const AirLoopNum)
     776              : {
     777              : 
     778              :     // SUBROUTINE INFORMATION:
     779              :     //       AUTHOR         Fred Buhl
     780              :     //       DATE WRITTEN   Oct 1998
     781              : 
     782              :     // PURPOSE OF THIS SUBROUTINE
     783              :     // Simulate an Outside Air Controller component
     784              : 
     785              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     786              :     int OAControllerNum;
     787              : 
     788        78418 :     if ((state.dataMixedAir->GetOAControllerInputFlag) &&
     789              :         (AirLoopNum > 0)) { // Gets input for object  first time Sim routine is called from an airloop
     790           25 :         GetOAControllerInputs(state);
     791           25 :         state.dataMixedAir->GetOAControllerInputFlag = false;
     792              :     }
     793              : 
     794              :     // check that the economizer staging operation EconomizerFirst is only used with an sensible load-based controlled AirLoopHVAC:UnitarySystem
     795        78418 :     if (AirLoopNum > 0) {
     796        78417 :         auto &primaryAirSystems = state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum);
     797        78417 :         if (primaryAirSystems.EconomizerStagingCheckFlag == false) {
     798           25 :             OAControllerNum = Util::FindItemInList(CtrlName, state.dataMixedAir->OAController);
     799           25 :             if (state.dataMixedAir->OAController(OAControllerNum).EconomizerStagingType == HVAC::EconomizerStagingType::EconomizerFirst) {
     800            0 :                 bool sensLoadCtrlUnitarySystemFound = false;
     801            0 :                 for (int BranchNum = 1; BranchNum <= primaryAirSystems.NumBranches; ++BranchNum) {
     802            0 :                     for (int CompNum = 1; CompNum <= primaryAirSystems.Branch(BranchNum).TotalComponents; ++CompNum) {
     803            0 :                         if (primaryAirSystems.Branch(BranchNum).Comp(CompNum).CompType_Num == SimAirServingZones::CompType::UnitarySystemModel) {
     804            0 :                             std::string_view unitarySystemName = primaryAirSystems.Branch(BranchNum).Comp(CompNum).Name;
     805            0 :                             int unitarySystemNum = Util::FindItemInList(
     806            0 :                                 unitarySystemName, state.dataUnitarySystems->unitarySys, state.dataUnitarySystems->numUnitarySystems);
     807            0 :                             if (state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_ControlType ==
     808              :                                 UnitarySystems::UnitarySys::UnitarySysCtrlType::Load) {
     809            0 :                                 if (state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num ==
     810            0 :                                         HVAC::CoilDX_MultiSpeedCooling ||
     811            0 :                                     state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num ==
     812            0 :                                         HVAC::Coil_CoolingAirToAirVariableSpeed ||
     813            0 :                                     state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_CoolingCoilType_Num == HVAC::CoilDX_Cooling) {
     814            0 :                                     sensLoadCtrlUnitarySystemFound = true;
     815            0 :                                     break;
     816              :                                 }
     817              :                             }
     818              :                         }
     819              :                     }
     820              :                 }
     821            0 :                 if (!sensLoadCtrlUnitarySystemFound) {
     822            0 :                     ShowWarningError(
     823              :                         state,
     824            0 :                         format(
     825              :                             "SimOAController: EconomizerFirst was selected in the \"{}\" Controller:OutdoorAir object but the air loop it belongs to "
     826              :                             "does not include an AirLoopHVAC:UnitarySystem with a \"Load\" Control Type input and cooling coil of one of the "
     827              :                             "following types: Coil:Cooling:DX:MultiSpeed,"
     828              :                             " Coil:Cooling:DX:VariableSpeed, or Coil:Cooling:DX. EconomizerFirst will not be enforced.",
     829            0 :                             state.dataMixedAir->OAController(OAControllerNum).Name));
     830              :                 }
     831              :             }
     832           25 :             primaryAirSystems.EconomizerStagingCheckFlag = true;
     833              :         }
     834              :     }
     835              : 
     836        78418 :     if (CtrlIndex == 0) {
     837           26 :         if (state.dataMixedAir->NumOAControllers > 0) {
     838           26 :             OAControllerNum = Util::FindItemInList(CtrlName, state.dataMixedAir->OAController);
     839              :         } else {
     840            0 :             OAControllerNum = 0;
     841              :         }
     842           26 :         CtrlIndex = OAControllerNum;
     843           26 :         if (OAControllerNum == 0) {
     844            0 :             ShowFatalError(state, format("SimOAController: Outside Air Controller not found={}", CtrlName));
     845              :         }
     846              :     } else {
     847        78392 :         OAControllerNum = CtrlIndex;
     848              :     }
     849              : 
     850        78418 :     InitOAController(state, OAControllerNum, FirstHVACIteration, AirLoopNum);
     851              : 
     852        78418 :     state.dataMixedAir->OAController(OAControllerNum).CalcOAController(state, AirLoopNum, FirstHVACIteration);
     853        78418 :     state.dataMixedAir->OAController(OAControllerNum).UpdateOAController(state);
     854        78418 : }
     855              : 
     856              : // Get Input Section of the Module
     857              : //******************************************************************************
     858              : 
     859          142 : void GetOutsideAirSysInputs(EnergyPlusData &state)
     860              : {
     861              : 
     862              :     // SUBROUTINE INFORMATION:
     863              :     //       AUTHOR         Fred Buhl
     864              :     //       DATE WRITTEN   Oct 1998
     865              : 
     866              :     // PURPOSE OF THIS SUBROUTINE
     867              :     // Input the Outside Air System data and store it in the OutsideAirSys array.
     868              : 
     869              :     // METHODOLOGY EMPLOYED:
     870              :     // Use the Get routines from the InputProcessor module.
     871              : 
     872              :     // SUBROUTINE PARAMETER DEFINITIONS:
     873              :     static constexpr std::string_view RoutineName("GetOutsideAirSysInputs: "); // include trailing blank space
     874              : 
     875              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     876          142 :     bool ErrorsFound(false);
     877              :     int NumNums;      // Number of real numbers returned by GetObjectItem
     878              :     int NumAlphas;    // Number of alphanumerics returned by GetObjectItem
     879          142 :     int TotalArgs(0); // Total number of alpha and numeric arguments (max) for a
     880              :     int IOStat;
     881          142 :     Array1D<Real64> NumArray;
     882          142 :     Array1D_string AlphArray;
     883          142 :     Array1D_string cAlphaFields;   // Alpha field names
     884          142 :     Array1D_string cNumericFields; // Numeric field names
     885          142 :     Array1D_bool lAlphaBlanks;     // Logical array, alpha field input BLANK = .TRUE.
     886          142 :     Array1D_bool lNumericBlanks;   // Logical array, numeric field input BLANK = .TRUE.
     887              : 
     888          142 :     if (!state.dataMixedAir->GetOASysInputFlag) {
     889            2 :         return;
     890              :     }
     891              : 
     892          280 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
     893          140 :         state, CurrentModuleObjects[static_cast<int>(CMO::OASystem)], TotalArgs, NumAlphas, NumNums);
     894          140 :     int MaxNums = NumNums;
     895          140 :     int MaxAlphas = NumAlphas;
     896          280 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
     897          140 :         state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], TotalArgs, NumAlphas, NumNums);
     898          140 :     MaxNums = max(MaxNums, NumNums);
     899          140 :     MaxAlphas = max(MaxAlphas, NumAlphas);
     900          280 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
     901          140 :         state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], TotalArgs, NumAlphas, NumNums);
     902          140 :     MaxNums = max(MaxNums, NumNums);
     903          140 :     MaxAlphas = max(MaxAlphas, NumAlphas);
     904              : 
     905          140 :     AlphArray.allocate(MaxAlphas);
     906          140 :     cAlphaFields.allocate(MaxAlphas);
     907          140 :     NumArray.dimension(MaxNums, 0.0);
     908          140 :     cNumericFields.allocate(MaxNums);
     909          140 :     lAlphaBlanks.dimension(MaxAlphas, true);
     910          140 :     lNumericBlanks.dimension(MaxNums, true);
     911              : 
     912          140 :     std::string_view CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::ControllerList)];
     913          140 :     state.dataMixedAir->NumControllerLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     914              : 
     915          140 :     state.dataMixedAir->ControllerLists.allocate(state.dataMixedAir->NumControllerLists);
     916              : 
     917          225 :     for (int Item = 1; Item <= state.dataMixedAir->NumControllerLists; ++Item) {
     918              : 
     919              :         // create a reference for convenience
     920           85 :         auto &thisControllerList(state.dataMixedAir->ControllerLists(Item));
     921           85 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     922              :                                                                  CurrentModuleObject,
     923              :                                                                  Item,
     924              :                                                                  AlphArray,
     925              :                                                                  NumAlphas,
     926              :                                                                  NumArray,
     927              :                                                                  NumNums,
     928              :                                                                  IOStat,
     929              :                                                                  lNumericBlanks,
     930              :                                                                  lAlphaBlanks,
     931              :                                                                  cAlphaFields,
     932              :                                                                  cNumericFields);
     933           85 :         thisControllerList.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
     934           85 :         thisControllerList.NumControllers = (NumAlphas - 1) / 2;
     935           85 :         thisControllerList.ControllerType.dimension(thisControllerList.NumControllers, ControllerKind::Invalid);
     936           85 :         thisControllerList.ControllerName.allocate(thisControllerList.NumControllers);
     937           85 :         int AlphaNum = 2;
     938          185 :         for (int CompNum = 1; CompNum <= thisControllerList.NumControllers; ++CompNum) {
     939              :             // Json will catch any object types that are not the correct key choice of Controller:OutdoorAir or Controller:WaterCoil
     940          200 :             thisControllerList.ControllerType(CompNum) =
     941          100 :                 static_cast<ControllerKind>(getEnumValue(ControllerKindNamesUC, Util::makeUPPER(AlphArray(AlphaNum))));
     942          100 :             thisControllerList.ControllerName(CompNum) = AlphArray(AlphaNum + 1);
     943              :             // loop over all previous controller lists to check if this controllers is also present on previous controllers
     944          139 :             for (int previousListNum = 1; previousListNum < Item; ++previousListNum) {
     945              :                 // loop over each of the controllers listed for this list
     946           39 :                 auto &previousList(state.dataMixedAir->ControllerLists(previousListNum));
     947           84 :                 for (int PreviousListControllerNum = 1; PreviousListControllerNum <= previousList.NumControllers; ++PreviousListControllerNum) {
     948           76 :                     if ((previousList.ControllerType(PreviousListControllerNum) == thisControllerList.ControllerType(CompNum)) &&
     949           31 :                         (previousList.ControllerName(PreviousListControllerNum) == thisControllerList.ControllerName(CompNum))) {
     950            0 :                         ShowSevereError(state, format("Controller instance repeated in multiple {} objects", CurrentModuleObject));
     951            0 :                         ShowContinueError(state, format("Found in {} = {}", CurrentModuleObject, thisControllerList.Name));
     952            0 :                         ShowContinueError(state, format("Also found in {} = {}", CurrentModuleObject, previousList.Name));
     953            0 :                         ErrorsFound = true;
     954              :                     }
     955              :                 }
     956              :             }
     957          100 :             AlphaNum += 2;
     958              :         }
     959              :     }
     960              : 
     961          140 :     CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OASystem)];
     962              : 
     963          140 :     state.dataAirLoop->NumOASystems = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     964              : 
     965          140 :     state.dataAirLoop->OutsideAirSys.allocate(state.dataAirLoop->NumOASystems);
     966          140 :     state.dataSize->OASysEqSizing.allocate(state.dataAirLoop->NumOASystems);
     967          140 :     state.dataMixedAir->ControllerListUniqueNames.reserve(static_cast<unsigned>(state.dataAirLoop->NumOASystems));
     968          140 :     state.dataMixedAir->MyOneTimeErrorFlag.dimension(state.dataAirLoop->NumOASystems, true);
     969          140 :     state.dataMixedAir->MyOneTimeCheckUnitarySysFlag.dimension(state.dataAirLoop->NumOASystems, true);
     970          140 :     state.dataMixedAir->initOASysFlag.dimension(state.dataAirLoop->NumOASystems, true);
     971              : 
     972          211 :     for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
     973           71 :         auto &OASys = state.dataAirLoop->OutsideAirSys(OASysNum);
     974           71 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     975              :                                                                  CurrentModuleObject,
     976              :                                                                  OASysNum,
     977              :                                                                  AlphArray,
     978              :                                                                  NumAlphas,
     979              :                                                                  NumArray,
     980              :                                                                  NumNums,
     981              :                                                                  IOStat,
     982              :                                                                  lNumericBlanks,
     983              :                                                                  lAlphaBlanks,
     984              :                                                                  cAlphaFields,
     985              :                                                                  cNumericFields);
     986           71 :         OASys.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
     987           71 :         if (!AlphArray(2).empty()) {
     988          134 :             GlobalNames::IntraObjUniquenessCheck(
     989           67 :                 state, AlphArray(2), CurrentModuleObject, cAlphaFields(2), state.dataMixedAir->ControllerListUniqueNames, ErrorsFound);
     990              :         }
     991           71 :         OASys.ControllerListName = AlphArray(2);
     992           71 :         OASys.ComponentListName = AlphArray(3);
     993              : 
     994          355 :         BranchNodeConnections::TestCompSet(state, CurrentModuleObject, OASys.Name, "UNDEFINED", "UNDEFINED", "Air Nodes");
     995              : 
     996           71 :         if (!lAlphaBlanks(3)) {
     997          142 :             int ListNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(
     998           71 :                 state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], OASys.ComponentListName);
     999           71 :             if (ListNum > 0) {
    1000          142 :                 state.dataInputProcessing->inputProcessor->getObjectItem(
    1001           71 :                     state, CurrentModuleObjects[static_cast<int>(CMO::AirLoopEqList)], ListNum, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
    1002           71 :                 int NumInList = (NumAlphas - 1) / 2;
    1003           71 :                 OASys.NumComponents = NumInList;
    1004           71 :                 OASys.ComponentName.allocate(NumInList);
    1005           71 :                 OASys.ComponentType.allocate(NumInList);
    1006           71 :                 OASys.ComponentTypeEnum.dimension(NumInList, SimAirServingZones::CompType::Invalid);
    1007           71 :                 OASys.ComponentIndex.dimension(NumInList, 0);
    1008           71 :                 OASys.InletNodeNum.dimension(NumInList, 0);
    1009           71 :                 OASys.OutletNodeNum.dimension(NumInList, 0);
    1010           71 :                 OASys.compPointer.resize(NumInList + 1, nullptr);
    1011          176 :                 for (int InListNum = 1; InListNum <= NumInList; ++InListNum) {
    1012          105 :                     OASys.ComponentName(InListNum) = AlphArray(InListNum * 2 + 1);
    1013          105 :                     OASys.ComponentType(InListNum) = AlphArray(InListNum * 2);
    1014              : 
    1015              :                     // Add equipment to component sets array
    1016          210 :                     BranchNodeConnections::SetUpCompSets(state,
    1017              :                                                          CurrentModuleObject,
    1018              :                                                          OASys.Name,
    1019          105 :                                                          OASys.ComponentType(InListNum),
    1020          105 :                                                          OASys.ComponentName(InListNum),
    1021              :                                                          "UNDEFINED",
    1022              :                                                          "UNDEFINED");
    1023              :                 }
    1024              :             } else {
    1025            0 :                 ShowSevereError(
    1026              :                     state,
    1027            0 :                     format("{} = \"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, OASys.Name, cAlphaFields(3), OASys.ComponentListName));
    1028            0 :                 ErrorsFound = true;
    1029              :             }
    1030              :         } else {
    1031            0 :             ShowSevereError(state, format("{} = \"{}\" invalid {} is blank and must be entered.", CurrentModuleObject, OASys.Name, cAlphaFields(3)));
    1032            0 :             ErrorsFound = true;
    1033              :         }
    1034              : 
    1035           71 :         int ListNum = 0;
    1036           71 :         int NumSimpControllers = 0; // number of Controller:Simple objects in an OA System
    1037           71 :         if (!lAlphaBlanks(2)) {
    1038          134 :             ListNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(
    1039           67 :                 state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], OASys.ControllerListName);
    1040           67 :             if (ListNum > 0) {
    1041          134 :                 state.dataInputProcessing->inputProcessor->getObjectItem(
    1042           67 :                     state, CurrentModuleObjects[static_cast<int>(CMO::ControllerList)], ListNum, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
    1043           67 :                 int NumInList = (NumAlphas - 1) / 2;
    1044           67 :                 OASys.NumControllers = NumInList;
    1045           67 :                 OASys.ControllerName.allocate(NumInList);
    1046           67 :                 OASys.ControllerType.allocate(NumInList);
    1047           67 :                 OASys.controllerTypeEnum.dimension(NumInList, DataAirLoop::ControllerKind::Invalid);
    1048           67 :                 OASys.ControllerIndex.dimension(NumInList, 0);
    1049          143 :                 for (int InListNum = 1; InListNum <= NumInList; ++InListNum) {
    1050           76 :                     OASys.ControllerName(InListNum) = AlphArray(InListNum * 2 + 1);
    1051           76 :                     OASys.ControllerType(InListNum) = AlphArray(InListNum * 2);
    1052          152 :                     OASys.controllerTypeEnum(InListNum) =
    1053           76 :                         static_cast<DataAirLoop::ControllerKind>(getEnumValue(ControllerKindNamesUC, OASys.ControllerType(InListNum)));
    1054              :                     // only count Controller:OutdoorAir types as valid simple controllers
    1055           76 :                     if (OASys.controllerTypeEnum(InListNum) != DataAirLoop::ControllerKind::OutdoorAir) {
    1056           12 :                         ++NumSimpControllers;
    1057              :                     }
    1058              :                 }
    1059              :             } else {
    1060            0 :                 ShowSevereError(state,
    1061            0 :                                 format("{} = \"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
    1062            0 :                 ErrorsFound = true;
    1063              :             }
    1064              :         } else {
    1065            4 :             if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:DedicatedOutdoorAirSystem") == 0) {
    1066            0 :                 ShowSevereError(state,
    1067            0 :                                 format("{} = \"{}\" invalid {} is blank and must be entered.", CurrentModuleObject, AlphArray(1), cAlphaFields(2)));
    1068            0 :                 ErrorsFound = true;
    1069              :             } else {
    1070            8 :                 ShowWarningError(state,
    1071            8 :                                  format("{} = \"{}\": blank {} must be used with AirLoopHVAC:DedicatedOutdoorAirSystem.",
    1072              :                                         CurrentModuleObject,
    1073              :                                         AlphArray(1),
    1074              :                                         cAlphaFields(2)));
    1075              :             }
    1076              :         }
    1077           71 :         OASys.ControllerListNum = ListNum;
    1078           71 :         OASys.NumSimpleControllers = NumSimpControllers;
    1079              :     }
    1080              : 
    1081          211 :     for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
    1082           71 :         auto &OASys = state.dataAirLoop->OutsideAirSys(OASysNum);
    1083          176 :         for (int CompNum = 1; CompNum <= OASys.NumComponents; ++CompNum) {
    1084          105 :             OASys.ComponentTypeEnum(CompNum) = static_cast<SimAirServingZones::CompType>(getEnumValue(CompTypeNamesUC, OASys.ComponentType(CompNum)));
    1085          105 :             if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::Fan_System_Object) {
    1086              :                 // construct fan object
    1087            5 :                 OASys.ComponentIndex(CompNum) = Fans::GetFanIndex(state, OASys.ComponentName(CompNum));
    1088          100 :             } else if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::CoilSystemWater ||
    1089          200 :                        OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::UnitarySystemModel ||
    1090          100 :                        OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::DXSystem) {
    1091            6 :                 OASys.ComponentIndex(CompNum) = CompNum;
    1092           94 :             } else if (OASys.ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::Invalid) {
    1093            8 :                 std::string const thisComp = OASys.ComponentType(CompNum);
    1094            8 :                 if (thisComp == "HEATEXCHANGER:AIRTOAIR:SENSIBLEANDLATENT" || thisComp == "HEATEXCHANGER:DESICCANT:BALANCEDFLOW") {
    1095            7 :                     OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::HeatXchngr;
    1096            1 :                 } else if (thisComp == "DEHUMIDIFIER:DESICCANT:SYSTEM") {
    1097            1 :                     OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::Desiccant;
    1098            0 :                 } else if (thisComp == "EVAPORATIVECOOLER:INDIRECT:CELDEKPAD" || thisComp == "EVAPORATIVECOOLER:INDIRECT:WETCOIL" ||
    1099            0 :                            thisComp == "EVAPORATIVECOOLER:INDIRECT:RESEARCHSPECIAL" || thisComp == "EVAPORATIVECOOLER:DIRECT:RESEARCHSPECIAL") {
    1100            0 :                     OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::EvapCooler;
    1101            0 :                 } else if (thisComp == "HUMIDIFIER:STEAM:GAS") {
    1102            0 :                     OASys.ComponentTypeEnum(CompNum) = SimAirServingZones::CompType::Humidifier;
    1103              :                 } else {
    1104            0 :                     ShowSevereError(
    1105              :                         state,
    1106            0 :                         format("{} = \"{}\" invalid Outside Air Component=\"{}\".", CurrentModuleObject, AlphArray(1), OASys.ComponentType(CompNum)));
    1107            0 :                     ErrorsFound = true;
    1108              :                 }
    1109            8 :             }
    1110              :         }
    1111              : 
    1112              :         // loop through the controllers in the controller list for OA system and save the pointer to the OA controller index
    1113           78 :         for (int OAControllerNum = 1; OAControllerNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers; ++OAControllerNum) {
    1114           71 :             if (state.dataAirLoop->OutsideAirSys(OASysNum).controllerTypeEnum(OAControllerNum) == DataAirLoop::ControllerKind::OutdoorAir) {
    1115           64 :                 state.dataAirLoop->OutsideAirSys(OASysNum).OAControllerName =
    1116          128 :                     state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerNum);
    1117           64 :                 break;
    1118              :             }
    1119              :         }
    1120              :     }
    1121              : 
    1122          140 :     if (ErrorsFound) {
    1123            0 :         ShowFatalError(state, format("{}Errors found in getting {}.", RoutineName, CurrentModuleObject));
    1124              :     }
    1125              : 
    1126          140 :     AlphArray.deallocate();
    1127          140 :     cAlphaFields.deallocate();
    1128          140 :     NumArray.deallocate();
    1129          140 :     cNumericFields.deallocate();
    1130          140 :     lAlphaBlanks.deallocate();
    1131          140 :     lNumericBlanks.deallocate();
    1132              : 
    1133          140 :     state.dataMixedAir->GetOASysInputFlag = false;
    1134              : 
    1135              :     // once GetOASysInputFlag is set to false other calls to objects can occur without worry that GetOutsideAirSysInputs will be called again
    1136              :     // now get the pointer for UnitarySystem - doing this earlier can cause recursion which trips IntraObjUniquenessCheck warnings
    1137          211 :     for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
    1138          176 :         for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents; ++CompNum) {
    1139          105 :             if (state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::CoilSystemWater ||
    1140          210 :                 state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::UnitarySystemModel ||
    1141          105 :                 state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(CompNum) == SimAirServingZones::CompType::DXSystem) {
    1142            6 :                 state.dataAirLoop->OutsideAirSys(OASysNum).compPointer[CompNum] = UnitarySystems::UnitarySys::factory(
    1143            6 :                     state, HVAC::UnitarySysType::Unitary_AnyCoilType, state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(CompNum), false, 0);
    1144              :             }
    1145              :         }
    1146              :     }
    1147          152 : }
    1148              : 
    1149           52 : void GetOAControllerInputs(EnergyPlusData &state)
    1150              : {
    1151              : 
    1152              :     // SUBROUTINE INFORMATION:
    1153              :     //       AUTHOR         Fred Buhl
    1154              :     //       DATE WRITTEN   Oct 1998
    1155              :     //       MODIFIED       Shirey/Raustad FSEC, June 2003, Jan 2004
    1156              :     //                      Mangesh Basarkar, 06/2011: Getting zone OA specifications from Design Specification Object
    1157              :     //                      Tianzhen Hong, 3/2012: getting zone air distribution effectiveness and secondary recirculation
    1158              :     //                       from DesignSpecification:ZoneAirDistribution objects
    1159              : 
    1160              :     // PURPOSE OF THIS SUBROUTINE
    1161              :     // Input the OAController data and store it in the OAController array.
    1162              :     // Input the Ventilation:Mechanical data and store it in the VentilationMechanical array.
    1163              :     //  Condense Ventilation:Mechanical data array to include only unique zones specified for each instance of this object.
    1164              : 
    1165              :     // METHODOLOGY EMPLOYED:
    1166              :     // Use the Get routines from the InputProcessor module.
    1167              : 
    1168              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1169              :     static constexpr std::string_view RoutineName("GetOAControllerInputs: "); // include trailing blank space
    1170              :     static constexpr std::string_view routineName = "GetOAControllerInputs";  // include trailing blank space
    1171              : 
    1172              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1173              :     int NumArg;    // Number of arguments from GetObjectDefMaxArgs call
    1174              :     int NumNums;   // Number of real numbers returned by GetObjectItem
    1175              :     int NumAlphas; // Number of alphanumerics returned by GetObjectItem
    1176              :     int IOStat;    // Status of GetObjectItem call
    1177           52 :     Array1D<Real64> NumArray;
    1178           52 :     Array1D_string AlphArray;
    1179           52 :     std::string_view CurrentModuleObject; // Object type for getting and messages
    1180           52 :     Array1D_string cAlphaFields;          // Alpha field names
    1181           52 :     Array1D_string cNumericFields;        // Numeric field names
    1182           52 :     Array1D_bool lAlphaBlanks;            // Logical array, alpha field input BLANK = .TRUE.
    1183           52 :     Array1D_bool lNumericBlanks;          // Logical array, numeric field input BLANK = .TRUE.
    1184           52 :     bool ErrorsFound(false);              // Flag identifying errors found during get input
    1185              : 
    1186              :     // First, call other get input routines in this module to make sure data is filled during this routine.
    1187           52 :     if (state.dataMixedAir->GetOASysInputFlag) { // Gets input for object  first time Sim routine is called
    1188           22 :         GetOutsideAirSysInputs(state);
    1189           22 :         state.dataMixedAir->GetOASysInputFlag = false;
    1190              :     }
    1191           52 :     if (state.dataMixedAir->GetOAMixerInputFlag) { // Gets input for object  first time Sim routine is called
    1192           29 :         GetOAMixerInputs(state);
    1193           29 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    1194              :     }
    1195              : 
    1196           52 :     FaultsManager::CheckAndReadFaults(state);
    1197              : 
    1198          104 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
    1199           52 :         state, CurrentModuleObjects[static_cast<int>(CMO::OAController)], NumArg, NumAlphas, NumNums);
    1200           52 :     int MaxAlphas = NumAlphas;
    1201           52 :     int MaxNums = NumNums;
    1202          104 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
    1203           52 :         state, CurrentModuleObjects[static_cast<int>(CMO::ERVController)], NumArg, NumAlphas, NumNums);
    1204           52 :     MaxAlphas = max(MaxAlphas, NumAlphas);
    1205           52 :     MaxNums = max(MaxNums, NumNums);
    1206          104 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
    1207           52 :         state, CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)], NumArg, NumAlphas, NumNums);
    1208           52 :     MaxAlphas = max(MaxAlphas, NumAlphas);
    1209           52 :     MaxNums = max(MaxNums, NumNums);
    1210              : 
    1211           52 :     AlphArray.allocate(MaxAlphas);
    1212           52 :     NumArray.dimension(MaxNums, 0.0);
    1213           52 :     lAlphaBlanks.dimension(MaxAlphas, true);
    1214           52 :     lNumericBlanks.dimension(MaxNums, true);
    1215           52 :     cAlphaFields.allocate(MaxAlphas);
    1216           52 :     cNumericFields.allocate(MaxNums);
    1217              : 
    1218              :     // Count OAcontrollers and ERVcontrollers and allocate arrays
    1219           52 :     AllocateOAControllers(state);
    1220              : 
    1221              :     // If there are ERV controllers, they have been filled before now NumOAControllers includes the count of NumERVControllers
    1222           52 :     if (state.dataMixedAir->NumOAControllers > state.dataMixedAir->NumERVControllers) {
    1223           46 :         CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OAController)];
    1224           46 :         int currentOAControllerNum = 0;
    1225           98 :         for (int OutAirNum = state.dataMixedAir->NumERVControllers + 1; OutAirNum <= state.dataMixedAir->NumOAControllers; ++OutAirNum) {
    1226           52 :             ++currentOAControllerNum;
    1227           52 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    1228              :                                                                      CurrentModuleObject,
    1229              :                                                                      currentOAControllerNum,
    1230              :                                                                      AlphArray,
    1231              :                                                                      NumAlphas,
    1232              :                                                                      NumArray,
    1233              :                                                                      NumNums,
    1234              :                                                                      IOStat,
    1235              :                                                                      lNumericBlanks,
    1236              :                                                                      lAlphaBlanks,
    1237              :                                                                      cAlphaFields,
    1238              :                                                                      cNumericFields);
    1239          104 :             GlobalNames::VerifyUniqueInterObjectName(
    1240           52 :                 state, state.dataMixedAir->OAControllerUniqueNames, AlphArray(1), CurrentModuleObject, cAlphaFields(1), ErrorsFound);
    1241              : 
    1242           52 :             ProcessOAControllerInputs(state,
    1243              :                                       CurrentModuleObject,
    1244              :                                       OutAirNum,
    1245              :                                       AlphArray,
    1246              :                                       NumAlphas,
    1247              :                                       NumArray,
    1248              :                                       NumNums,
    1249              :                                       lNumericBlanks,
    1250              :                                       lAlphaBlanks,
    1251              :                                       cAlphaFields,
    1252              :                                       cNumericFields,
    1253              :                                       ErrorsFound);
    1254              : 
    1255              :             // add applicable faults identifier to avoid string comparison at each time step
    1256              :             //  loop through each fault for each OA controller and determine economizer faults
    1257           62 :             for (int i = 1; i <= state.dataFaultsMgr->NumFaultyEconomizer; ++i) {
    1258           10 :                 if (state.dataFaultsMgr->FaultsEconomizer(i).ControllerTypeEnum != iController_AirEconomizer) {
    1259            0 :                     continue;
    1260              :                 }
    1261           10 :                 if (Util::SameString(state.dataMixedAir->OAController(OutAirNum).Name, state.dataFaultsMgr->FaultsEconomizer(i).ControllerName)) {
    1262            5 :                     state.dataFaultsMgr->FaultsEconomizer(i).ControllerID = OutAirNum;
    1263            5 :                     ++state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer;
    1264              :                 }
    1265              :             }
    1266              :             //  loop through each fault for each OA controller to determine faulty counts
    1267           52 :             state.dataMixedAir->OAController(OutAirNum).EconmizerFaultNum.allocate(state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer);
    1268           52 :             if (state.dataMixedAir->OAController(OutAirNum).NumFaultyEconomizer > 0) {
    1269           12 :                 for (int j = 0, i = 1; i <= state.dataFaultsMgr->NumFaultyEconomizer; ++i) {
    1270           10 :                     if (state.dataFaultsMgr->FaultsEconomizer(i).ControllerTypeEnum != iController_AirEconomizer) {
    1271            0 :                         continue;
    1272              :                     }
    1273           10 :                     if (Util::SameString(state.dataMixedAir->OAController(OutAirNum).Name, state.dataFaultsMgr->FaultsEconomizer(i).ControllerName)) {
    1274            5 :                         state.dataMixedAir->OAController(OutAirNum).EconmizerFaultNum(++j) = i;
    1275              :                     }
    1276              :                 }
    1277              :             }
    1278              :         } // LOOP FOR OutAirNum
    1279              : 
    1280           46 :         if (ErrorsFound) {
    1281            0 :             AlphArray.deallocate();
    1282            0 :             NumArray.deallocate();
    1283            0 :             lNumericBlanks.deallocate();
    1284            0 :             lAlphaBlanks.deallocate();
    1285            0 :             cAlphaFields.deallocate();
    1286            0 :             cNumericFields.deallocate();
    1287            0 :             ShowFatalError(state, format("{}Errors found in getting {} inputs.", RoutineName, CurrentModuleObject));
    1288              :         }
    1289              :     }
    1290              : 
    1291           52 :     state.dataMixedAir->GetOAControllerInputFlag = false;
    1292              : 
    1293              :     // Process Controller:MechanicalVentilation objects
    1294           52 :     CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)];
    1295           52 :     state.dataMixedAir->NumVentMechControllers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
    1296           52 :     if (state.dataMixedAir->NumVentMechControllers > 0) {
    1297           16 :         state.dataMixedAir->VentilationMechanical.allocate(state.dataMixedAir->NumVentMechControllers);
    1298           32 :         for (int VentMechNum = 1; VentMechNum <= state.dataMixedAir->NumVentMechControllers; ++VentMechNum) {
    1299           16 :             auto &thisVentilationMechanical(state.dataMixedAir->VentilationMechanical(VentMechNum));
    1300           16 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    1301              :                                                                      CurrentModuleObject,
    1302              :                                                                      VentMechNum,
    1303              :                                                                      AlphArray,
    1304              :                                                                      NumAlphas,
    1305              :                                                                      NumArray,
    1306              :                                                                      NumNums,
    1307              :                                                                      IOStat,
    1308              :                                                                      lNumericBlanks,
    1309              :                                                                      lAlphaBlanks,
    1310              :                                                                      cAlphaFields,
    1311              :                                                                      cNumericFields);
    1312              : 
    1313           16 :             ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
    1314              : 
    1315           16 :             int MechVentZoneCount = 0;
    1316              : 
    1317           16 :             int NumGroups = (NumAlphas + NumNums - 5) / 3; // Number of extensible input groups of the VentilationMechanical object
    1318           16 :             if (mod((NumAlphas + NumNums - 5), 3) != 0) {
    1319            3 :                 ++NumGroups;
    1320              :             }
    1321           16 :             thisVentilationMechanical.Name = AlphArray(1); // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
    1322              : 
    1323           16 :             if (lAlphaBlanks(2)) {
    1324            5 :                 thisVentilationMechanical.availSched = Sched::GetScheduleAlwaysOn(state);
    1325           11 :             } else if ((thisVentilationMechanical.availSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
    1326            0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFields(2), AlphArray(2));
    1327            0 :                 ErrorsFound = true;
    1328              :             }
    1329              : 
    1330              :             // Adding new flag for DCV
    1331           16 :             if (Util::SameString(AlphArray(3), "Yes")) {
    1332           11 :                 thisVentilationMechanical.DCVFlag = true;
    1333            5 :             } else if (Util::SameString(AlphArray(3), "No") || lAlphaBlanks(3)) {
    1334            5 :                 thisVentilationMechanical.DCVFlag = false;
    1335              :             } else {
    1336            0 :                 ShowSevereError(state,
    1337            0 :                                 format("{}=\"{}\" invalid value {}=\"{}\".", CurrentModuleObject, AlphArray(1), cAlphaFields(3), AlphArray(3)));
    1338            0 :                 ShowContinueError(state, "...Valid values are \"Yes\" or \"No\".");
    1339            0 :                 ErrorsFound = true;
    1340              :             }
    1341              : 
    1342              :             // System outdoor air method
    1343           16 :             thisVentilationMechanical.SystemOAMethod = static_cast<DataSizing::SysOAMethod>(getEnumValue(SOAMNamesUC, Util::makeUPPER(AlphArray(4))));
    1344              : 
    1345           16 :             if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQP ||
    1346           15 :                 thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
    1347           15 :                 thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
    1348           13 :                 thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate ||
    1349           12 :                 thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
    1350            4 :                 if (!state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1351            0 :                     ShowSevereError(
    1352              :                         state,
    1353            0 :                         format(
    1354              :                             "{}=\"{}\" valid {}=\"{}\" requires CO2 simulation.", CurrentModuleObject, AlphArray(1), cAlphaFields(2), AlphArray(2)));
    1355            0 :                     ShowContinueError(state, "The choice must be Yes for the field Carbon Dioxide Concentration in ZoneAirContaminantBalance");
    1356            0 :                     ErrorsFound = true;
    1357              :                 }
    1358              :             }
    1359              : 
    1360           16 :             if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPGC ||
    1361           16 :                 thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
    1362            0 :                 if (!state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1363            0 :                     ShowSevereError(state,
    1364            0 :                                     format("{}=\"{}\" valid {}=\"{}\" requires generic contaminant simulation.",
    1365              :                                            CurrentModuleObject,
    1366              :                                            AlphArray(1),
    1367              :                                            cAlphaFields(2),
    1368              :                                            AlphArray(2)));
    1369            0 :                     ShowContinueError(state, "The choice must be Yes for the field Generic Contaminant Concentration in ZoneAirContaminantBalance");
    1370            0 :                     ErrorsFound = true;
    1371              :                 }
    1372              :             }
    1373              : 
    1374           16 :             if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::Invalid) { // If specified incorrectly, show errors
    1375            0 :                 thisVentilationMechanical.SystemOAMethod = DataSizing::SysOAMethod::ZoneSum;
    1376            0 :                 ShowWarningError(state,
    1377            0 :                                  format("{}=\"{}\" incorrect specification for {}, the ZoneSum method will be used.",
    1378              :                                         CurrentModuleObject,
    1379              :                                         AlphArray(1),
    1380              :                                         cAlphaFields(4)));
    1381              :                 // ErrorsFound=.TRUE.
    1382              :             }
    1383              : 
    1384              :             // Zone maximum outdoor air fraction
    1385           16 :             thisVentilationMechanical.ZoneMaxOAFraction = NumArray(1);
    1386              : 
    1387           16 :             state.dataMixedAir->VentMechZoneOrListName.allocate(NumGroups);
    1388           16 :             state.dataMixedAir->DesignSpecOAObjName.allocate(NumGroups);
    1389           16 :             state.dataMixedAir->DesignSpecOAObjIndex.dimension(NumGroups, 0);
    1390           16 :             state.dataMixedAir->DesignSpecZoneADObjName.allocate(NumGroups);
    1391           16 :             state.dataMixedAir->DesignSpecZoneADObjIndex.dimension(NumGroups, 0);
    1392              : 
    1393              :             //   First time through find the total number of zones requiring mechanical ventilation
    1394              :             //   May include duplicate zones. Will check for duplicate zones further down in this subroutine.
    1395           48 :             for (int groupNum = 1; groupNum <= NumGroups; ++groupNum) {
    1396           32 :                 state.dataMixedAir->VentMechZoneOrListName(groupNum) = AlphArray((groupNum - 1) * 3 + 5);
    1397              : 
    1398              :                 //     Getting OA details from design specification OA object
    1399           32 :                 if (!lAlphaBlanks((groupNum - 1) * 3 + 6)) {
    1400           29 :                     state.dataMixedAir->DesignSpecOAObjName(groupNum) = AlphArray((groupNum - 1) * 3 + 6);
    1401           29 :                     int ObjIndex = Util::FindItemInList(state.dataMixedAir->DesignSpecOAObjName(groupNum), state.dataSize->OARequirements);
    1402           29 :                     state.dataMixedAir->DesignSpecOAObjIndex(groupNum) = ObjIndex;
    1403              : 
    1404           29 :                     if (ObjIndex == 0) {
    1405            0 :                         ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
    1406            0 :                         ShowContinueError(state,
    1407            0 :                                           format("... not found {}=\"{}\".",
    1408            0 :                                                  cAlphaFields((groupNum - 1) * 3 + 6),
    1409            0 :                                                  state.dataMixedAir->DesignSpecOAObjName(groupNum)));
    1410            0 :                         ErrorsFound = true;
    1411              :                     }
    1412              :                 }
    1413              : 
    1414              :                 // Get zone air distribution details from design specification Zone Air Distribution object
    1415           32 :                 if (!lAlphaBlanks((groupNum - 1) * 3 + 7)) {
    1416           19 :                     state.dataMixedAir->DesignSpecZoneADObjName(groupNum) = AlphArray((groupNum - 1) * 3 + 7);
    1417           19 :                     int ObjIndex = Util::FindItemInList(state.dataMixedAir->DesignSpecZoneADObjName(groupNum), state.dataSize->ZoneAirDistribution);
    1418           19 :                     state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) = ObjIndex;
    1419              : 
    1420           19 :                     if (ObjIndex == 0) {
    1421              :                         // Cannot find the design specification Zone Air Distribution object
    1422            0 :                         ShowSevereError(state, format("{}{}=\"{}\", invalid", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
    1423            0 :                         ShowContinueError(state,
    1424            0 :                                           format("... not found {}=\"{}\".",
    1425            0 :                                                  cAlphaFields((groupNum - 1) * 3 + 7),
    1426            0 :                                                  state.dataMixedAir->DesignSpecZoneADObjName(groupNum)));
    1427            0 :                         ErrorsFound = true;
    1428              :                     }
    1429              :                 }
    1430              : 
    1431           32 :                 int ZoneNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->Zone);
    1432           32 :                 if (ZoneNum > 0) {
    1433           32 :                     ++MechVentZoneCount;
    1434              :                 } else {
    1435            0 :                     int ZoneListNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->ZoneList);
    1436            0 :                     if (ZoneListNum > 0) {
    1437            0 :                         MechVentZoneCount += state.dataHeatBal->ZoneList(ZoneListNum).NumOfZones;
    1438              :                     } else {
    1439            0 :                         ShowWarningError(
    1440              :                             state,
    1441            0 :                             format("{}=\"{}\" invalid {} not found.", CurrentModuleObject, AlphArray(1), cAlphaFields((groupNum - 1) * 3 + 5)));
    1442            0 :                         ShowContinueError(
    1443              :                             state,
    1444            0 :                             format("Missing {} = {}", cAlphaFields((groupNum - 1) * 3 + 5), state.dataMixedAir->VentMechZoneOrListName(groupNum)));
    1445            0 :                         ErrorsFound = true;
    1446              :                     }
    1447              :                 }
    1448              :             }
    1449              : 
    1450           16 :             thisVentilationMechanical.NumofVentMechZones = MechVentZoneCount;
    1451              : 
    1452              :             // Now allocate and store unique zone and associated ventilation rate information
    1453           16 :             thisVentilationMechanical.VentMechZone.allocate(MechVentZoneCount);
    1454              : 
    1455           16 :             MechVentZoneCount = 0;
    1456              : 
    1457              :             //   Loop through zone names and list of zone names, remove duplicate zones, and store designspec names and indexes
    1458           48 :             for (int groupNum = 1; groupNum <= NumGroups; ++groupNum) {
    1459           32 :                 int ZoneNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->Zone);
    1460           32 :                 if (ZoneNum > 0) {
    1461           32 :                     if (std::any_of(thisVentilationMechanical.VentMechZone.begin(),
    1462           64 :                                     thisVentilationMechanical.VentMechZone.end(),
    1463           86 :                                     [ZoneNum](auto const &vmZone) { return vmZone.zoneNum == ZoneNum; })) {
    1464              :                         //          Disregard duplicate zone names, show warning and do not store data for this zone
    1465            0 :                         ShowWarningError(state,
    1466            0 :                                          format("Zone name = {} for {} object = {}",
    1467            0 :                                                 state.dataMixedAir->VentMechZoneOrListName(groupNum),
    1468              :                                                 CurrentModuleObject,
    1469            0 :                                                 thisVentilationMechanical.Name));
    1470            0 :                         ShowContinueError(state, "is specified more than once. The first ventilation values specified for this zone will be used");
    1471            0 :                         ShowContinueError(state, "and the rest will be ignored. Simulation will continue..");
    1472              :                     } else {
    1473              :                         //          Store unique zone names
    1474           32 :                         ++MechVentZoneCount;
    1475           32 :                         auto &thisMechVentZone = thisVentilationMechanical.VentMechZone(MechVentZoneCount);
    1476           32 :                         thisMechVentZone.zoneNum = ZoneNum;
    1477           32 :                         thisMechVentZone.name = state.dataHeatBal->Zone(ZoneNum).Name;
    1478              : 
    1479              :                         // Populating new temp array to hold design spec OA object for each zone
    1480           32 :                         if (state.dataMixedAir->DesignSpecOAObjIndex(groupNum) > 0) {
    1481           29 :                             thisMechVentZone.ZoneDesignSpecOAObjName = state.dataMixedAir->DesignSpecOAObjName(groupNum);
    1482           29 :                             thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataMixedAir->DesignSpecOAObjIndex(groupNum);
    1483              :                         } else {
    1484            3 :                             if (state.dataGlobal->DoZoneSizing) {
    1485            1 :                                 int ObjIndex = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum),
    1486            1 :                                                                     state.dataSize->ZoneSizingInput,
    1487              :                                                                     &ZoneSizingInputData::ZoneName);
    1488            1 :                                 if (ObjIndex > 0) {
    1489            1 :                                     thisMechVentZone.ZoneDesignSpecOAObjName = state.dataSize->ZoneSizingInput(ObjIndex).DesignSpecOAObjName;
    1490            1 :                                     thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataSize->ZoneSizingInput(ObjIndex).ZoneDesignSpecOAIndex;
    1491              :                                 }
    1492              :                             }
    1493              :                         }
    1494              :                         // Zone Air Distribution inputs
    1495           32 :                         if (state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) > 0) {
    1496              :                             // new DCV inputs
    1497           19 :                             thisMechVentZone.ZoneDesignSpecADObjName = state.dataMixedAir->DesignSpecZoneADObjName(groupNum);
    1498           19 :                             thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum);
    1499              :                         } else {
    1500           13 :                             if (state.dataGlobal->DoZoneSizing) {
    1501            0 :                                 int ObjIndex = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum),
    1502            0 :                                                                     state.dataSize->ZoneSizingInput,
    1503              :                                                                     &ZoneSizingInputData::ZoneName);
    1504            0 :                                 if (ObjIndex > 0) {
    1505            0 :                                     thisMechVentZone.ZoneDesignSpecADObjName = state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistEffObjName;
    1506            0 :                                     thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistributionIndex;
    1507              :                                 }
    1508              :                             }
    1509              :                         }
    1510              :                     }
    1511              :                 } else {
    1512              :                     //       Not a zone name, must be a zone list
    1513            0 :                     int ZoneListNum = Util::FindItemInList(state.dataMixedAir->VentMechZoneOrListName(groupNum), state.dataHeatBal->ZoneList);
    1514            0 :                     if (ZoneListNum > 0) {
    1515            0 :                         for (int ScanZoneListNum = 1; ScanZoneListNum <= state.dataHeatBal->ZoneList(ZoneListNum).NumOfZones; ++ScanZoneListNum) {
    1516              :                             // check to make sure zone name is unique (not listed more than once)...
    1517            0 :                             int zoneNum2 = state.dataHeatBal->ZoneList(ZoneListNum).Zone(ScanZoneListNum);
    1518            0 :                             if (std::any_of(thisVentilationMechanical.VentMechZone.begin(),
    1519            0 :                                             thisVentilationMechanical.VentMechZone.end(),
    1520            0 :                                             [zoneNum2](auto const &vmZone) { return vmZone.zoneNum == zoneNum2; })) {
    1521              :                                 //             Disregard duplicate zone names, show warning and do not store data for this zone
    1522            0 :                                 ShowWarningError(state,
    1523            0 :                                                  format("Zone name = {} in ZoneList = {} for {} object = {}",
    1524            0 :                                                         state.dataHeatBal->Zone(zoneNum2).Name,
    1525            0 :                                                         state.dataMixedAir->VentMechZoneOrListName(groupNum),
    1526              :                                                         CurrentModuleObject,
    1527            0 :                                                         thisVentilationMechanical.Name));
    1528            0 :                                 ShowContinueError(state, "is a duplicate. The first ventilation values specified for this zone will be used ");
    1529            0 :                                 ShowContinueError(state, "and the rest will be ignored. The simulation will continue...");
    1530              :                             } else {
    1531              :                                 //           Store data for each zone name from zone list (duplicate zone names accounted for in
    1532              :                                 //           HeatBalanceManager)
    1533            0 :                                 ++MechVentZoneCount;
    1534            0 :                                 auto &thisMechVentZone = thisVentilationMechanical.VentMechZone(MechVentZoneCount);
    1535            0 :                                 thisMechVentZone.zoneNum = zoneNum2;
    1536            0 :                                 thisMechVentZone.name = state.dataHeatBal->Zone(zoneNum2).Name;
    1537              :                                 // Populating new temp array to hold design spec OA object for each zone
    1538            0 :                                 if (state.dataMixedAir->DesignSpecOAObjIndex(groupNum) > 0) {
    1539            0 :                                     thisMechVentZone.ZoneDesignSpecOAObjName = state.dataMixedAir->DesignSpecOAObjName(groupNum);
    1540            0 :                                     thisMechVentZone.ZoneDesignSpecOAObjIndex = state.dataMixedAir->DesignSpecOAObjIndex(groupNum);
    1541              :                                 } else {
    1542            0 :                                     if (state.dataGlobal->DoZoneSizing) {
    1543            0 :                                         int ObjIndex = Util::FindItemInList(
    1544            0 :                                             state.dataHeatBal->Zone(zoneNum2).Name, state.dataSize->ZoneSizingInput, &ZoneSizingInputData::ZoneName);
    1545            0 :                                         if (ObjIndex > 0) {
    1546            0 :                                             thisMechVentZone.ZoneDesignSpecOAObjName = state.dataSize->ZoneSizingInput(ObjIndex).DesignSpecOAObjName;
    1547            0 :                                             thisMechVentZone.ZoneDesignSpecOAObjIndex =
    1548            0 :                                                 state.dataSize->ZoneSizingInput(ObjIndex).ZoneDesignSpecOAIndex;
    1549              :                                         }
    1550              :                                     }
    1551              :                                 }
    1552              : 
    1553            0 :                                 if (state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum) > 0) {
    1554              :                                     // new DCV inputs
    1555            0 :                                     thisMechVentZone.ZoneDesignSpecADObjName = state.dataMixedAir->DesignSpecZoneADObjName(groupNum);
    1556            0 :                                     thisMechVentZone.ZoneDesignSpecADObjIndex = state.dataMixedAir->DesignSpecZoneADObjIndex(groupNum);
    1557              :                                 } else {
    1558            0 :                                     if (state.dataGlobal->DoZoneSizing) {
    1559            0 :                                         int ObjIndex = Util::FindItemInList(
    1560            0 :                                             state.dataHeatBal->Zone(zoneNum2).Name, state.dataSize->ZoneSizingInput, &ZoneSizingInputData::ZoneName);
    1561            0 :                                         if (ObjIndex > 0) {
    1562              :                                             thisMechVentZone.ZoneDesignSpecADObjName =
    1563            0 :                                                 state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistEffObjName;
    1564            0 :                                             thisMechVentZone.ZoneDesignSpecADObjIndex =
    1565            0 :                                                 state.dataSize->ZoneSizingInput(ObjIndex).ZoneAirDistributionIndex;
    1566              :                                         }
    1567              :                                     }
    1568              :                                 }
    1569              :                             }
    1570              :                         }
    1571              :                     }
    1572              :                 }
    1573              :             }
    1574              : 
    1575           16 :             if (MechVentZoneCount <= 0) {
    1576            0 :                 ShowSevereError(state,
    1577            0 :                                 format("{}{}=\"{}\", invalid input. At least one Zone or ZoneList Name must be entered.",
    1578              :                                        RoutineName,
    1579              :                                        CurrentModuleObject,
    1580            0 :                                        thisVentilationMechanical.Name));
    1581            0 :                 ErrorsFound = true;
    1582              :             }
    1583              : 
    1584              :             //   Overwrite previous number of zones with number that does not include duplicates
    1585           16 :             thisVentilationMechanical.NumofVentMechZones = MechVentZoneCount;
    1586              : 
    1587              :             // Loop over zones and fill OA and AD specs, if none were found, use defaults
    1588           48 :             for (int ventMechZoneNum = 1; ventMechZoneNum <= MechVentZoneCount; ++ventMechZoneNum) {
    1589           32 :                 auto &thisVentMechZone = thisVentilationMechanical.VentMechZone(ventMechZoneNum);
    1590           32 :                 int zoneOAReqObjIndex = thisVentMechZone.ZoneDesignSpecOAObjIndex;
    1591           32 :                 if (zoneOAReqObjIndex > 0) {
    1592           29 :                     auto const &curOARequirements(state.dataSize->OARequirements(zoneOAReqObjIndex));
    1593           29 :                     thisVentMechZone.ZoneOAAreaRate = curOARequirements.OAFlowPerArea;
    1594           29 :                     thisVentMechZone.ZoneOAPeopleRate = curOARequirements.OAFlowPerPerson;
    1595           29 :                     thisVentMechZone.ZoneOAFlowRate = curOARequirements.OAFlowPerZone;
    1596           29 :                     thisVentMechZone.ZoneOAACHRate = curOARequirements.OAFlowACH;
    1597           29 :                     thisVentMechZone.ZoneOAFlowMethod = curOARequirements.OAFlowMethod;
    1598           29 :                     thisVentMechZone.zoneOASched = curOARequirements.oaFlowFracSched;
    1599           29 :                     thisVentMechZone.oaPropCtlMinRateSched = curOARequirements.oaPropCtlMinRateSched;
    1600           29 :                     if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
    1601            1 :                         if (thisVentMechZone.ZoneOAPeopleRate == 0.0 && thisVentMechZone.ZoneOAAreaRate == 0.0) {
    1602            0 :                             ShowSevereError(
    1603              :                                 state,
    1604            0 :                                 format("{}{}=\"{}\", invalid input with System Outdoor Air Method = ProportionalControlBasedOnDesignOARate.",
    1605              :                                        RoutineName,
    1606              :                                        CurrentModuleObject,
    1607            0 :                                        thisVentilationMechanical.Name));
    1608            0 :                             ShowContinueError(state,
    1609              :                                               " The values of Outdoor Air Flow per Person and Outdoor Air Flow per Zone Floor Area in the same "
    1610              :                                               "object can not be zero.");
    1611            0 :                             ErrorsFound = true;
    1612              :                         }
    1613              :                     }
    1614              :                 } else { // use defaults
    1615            3 :                     thisVentMechZone.ZoneOAAreaRate = 0.0;
    1616              :                     // since this is case with no DesSpcOA object, cannot determine the method and default would be Flow/Person which should
    1617              :                     // default to this flow rate
    1618            3 :                     thisVentMechZone.ZoneOAPeopleRate = 0.00944;
    1619            3 :                     thisVentMechZone.ZoneOAFlowRate = 0.0;
    1620            3 :                     thisVentMechZone.ZoneOAACHRate = 0.0;
    1621            3 :                     thisVentMechZone.ZoneOAFlowMethod = OAFlowCalcMethod::PerPerson;
    1622            3 :                     thisVentMechZone.zoneOASched =
    1623            3 :                         Sched::GetScheduleAlwaysOn(state); // defaults to constant-1.0. TODO: what is this really suppoed to be?
    1624            3 :                     ShowWarningError(state, format("{}{}=\"{}", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
    1625            6 :                     ShowContinueError(
    1626            6 :                         state, format("Cannot locate a matching DesignSpecification:OutdoorAir object for Zone=\"{}\".", thisVentMechZone.name));
    1627            9 :                     ShowContinueError(state, "Using default OA of 0.00944 m3/s-person and 0.0 m3/s-m2.");
    1628              :                 }
    1629           32 :                 int zoneAirDistObjIndex = thisVentMechZone.ZoneDesignSpecADObjIndex;
    1630           32 :                 if (zoneAirDistObjIndex > 0) {
    1631           19 :                     auto const &curZoneAirDistribution(state.dataSize->ZoneAirDistribution(zoneAirDistObjIndex));
    1632           19 :                     thisVentMechZone.ZoneADEffCooling = curZoneAirDistribution.ZoneADEffCooling;
    1633           19 :                     thisVentMechZone.ZoneADEffHeating = curZoneAirDistribution.ZoneADEffHeating;
    1634           19 :                     thisVentMechZone.zoneADEffSched = curZoneAirDistribution.zoneADEffSched;
    1635           19 :                     thisVentMechZone.ZoneSecondaryRecirculation = curZoneAirDistribution.ZoneSecondaryRecirculation;
    1636              :                 } else { // use defaults
    1637           13 :                     thisVentMechZone.ZoneADEffCooling = 1.0;
    1638           13 :                     thisVentMechZone.ZoneADEffHeating = 1.0;
    1639           13 :                     thisVentMechZone.ZoneSecondaryRecirculation = 0.0;
    1640           13 :                     ShowWarningError(state, format("{}{}=\"{}\"", RoutineName, CurrentModuleObject, thisVentilationMechanical.Name));
    1641           26 :                     ShowContinueError(
    1642              :                         state,
    1643           26 :                         format("Cannot locate a matching DesignSpecification:ZoneAirDistribution object for Zone=\"{}\".", thisVentMechZone.name));
    1644           39 :                     ShowContinueError(state, "Using default zone air distribution effectiveness of 1.0 for heating and cooling.");
    1645              :                 }
    1646              :             }
    1647           16 :             state.dataMixedAir->VentMechZoneOrListName.deallocate();
    1648           16 :             state.dataMixedAir->DesignSpecOAObjName.deallocate();
    1649           16 :             state.dataMixedAir->DesignSpecOAObjIndex.deallocate();
    1650           16 :             state.dataMixedAir->DesignSpecZoneADObjName.deallocate();
    1651           16 :             state.dataMixedAir->DesignSpecZoneADObjIndex.deallocate();
    1652              :         }
    1653              : 
    1654           32 :         for (int VentMechNum = 1; VentMechNum <= state.dataMixedAir->NumVentMechControllers; ++VentMechNum) {
    1655           16 :             auto &thisVentilationMechanical(state.dataMixedAir->VentilationMechanical(VentMechNum));
    1656           48 :             for (int jZone = 1; jZone <= thisVentilationMechanical.NumofVentMechZones; ++jZone) {
    1657           32 :                 auto &thisVentMechZone = thisVentilationMechanical.VentMechZone(jZone);
    1658           32 :                 if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
    1659            0 :                     if (thisVentMechZone.ZoneOAACHRate > 0.0 || thisVentMechZone.ZoneOAFlowRate > 0.0) {
    1660            0 :                         ShowWarningError(state,
    1661            0 :                                          format("{}=\"{}\", inappropriate outdoor air method", CurrentModuleObject, thisVentilationMechanical.Name));
    1662            0 :                         ShowContinueError(state,
    1663            0 :                                           format("Inappropriate method for Design Specification Outdoor Air Object Name=\"{}\".",
    1664            0 :                                                  thisVentMechZone.ZoneDesignSpecOAObjName));
    1665            0 :                         ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1666            0 :                         ShowContinueError(state,
    1667              :                                           "Since System Outdoor Air Method= ProportionalControlBasedOnOccupancySchedule\", AirChanges/Hour or "
    1668              :                                           "Flow/Zone outdoor air methods are not valid. Simulation continues.... ");
    1669              :                     }
    1670              :                 }
    1671           32 :                 if (thisVentilationMechanical.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    1672            4 :                     if (thisVentMechZone.ZoneOAACHRate > 0.0 || thisVentMechZone.ZoneOAFlowRate > 0.0) {
    1673            0 :                         ShowWarningError(state,
    1674            0 :                                          format("{}=\"{}\", inappropriate outdoor air method", CurrentModuleObject, thisVentilationMechanical.Name));
    1675            0 :                         ShowContinueError(state,
    1676            0 :                                           format("Inappropriate method for Design Specification Outdoor Air Object Name=\"{}\".",
    1677            0 :                                                  thisVentMechZone.ZoneDesignSpecOAObjName));
    1678            0 :                         ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1679            0 :                         ShowContinueError(state,
    1680              :                                           "Since System Outdoor Air Method= ProportionalControlBasedOnDesignOccupancy\", AirChanges/Hour or "
    1681              :                                           "Flow/Zone outdoor air methods are not valid. Simulation continues.... ");
    1682              :                     }
    1683              :                 }
    1684              : 
    1685              :                 // Error check to see if a single duct air terminal is assigned to a zone that has zone secondary recirculation
    1686           32 :                 if (thisVentMechZone.ZoneSecondaryRecirculation > 0.0) {
    1687            0 :                     int ZoneNum = thisVentMechZone.zoneNum;
    1688            0 :                     if (ZoneNum > 0) {
    1689            0 :                         int EquipListIndex = state.dataZoneEquip->ZoneEquipConfig(ZoneNum).EquipListIndex;
    1690            0 :                         if (EquipListIndex > 0) {
    1691            0 :                             for (int EquipListNum = 1; EquipListNum <= state.dataZoneEquip->NumOfZoneEquipLists; ++EquipListNum) {
    1692            0 :                                 if (EquipListNum == EquipListIndex) {
    1693            0 :                                     for (int EquipNum = 1; EquipNum <= state.dataZoneEquip->ZoneEquipList(EquipListNum).NumOfEquipTypes; ++EquipNum) {
    1694            0 :                                         if (Util::SameString(state.dataZoneEquip->ZoneEquipList(EquipListNum).EquipTypeName(EquipNum),
    1695              :                                                              "ZONEHVAC:AIRDISTRIBUTIONUNIT")) {
    1696            0 :                                             for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
    1697            0 :                                                 if (Util::SameString(state.dataZoneEquip->ZoneEquipList(EquipListNum).EquipName(EquipNum),
    1698            0 :                                                                      state.dataDefineEquipment->AirDistUnit(ADUNum).Name)) {
    1699            0 :                                                     if ((state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1700            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVReheat) ||
    1701            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1702            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolNoReheat) ||
    1703            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1704            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolReheat) ||
    1705            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1706            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVNoReheat) ||
    1707            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1708            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctVAVReheatVSFan) ||
    1709            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1710            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctCBVAVReheat) ||
    1711            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1712            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctCBVAVNoReheat) ||
    1713            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1714            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolCooledBeam) ||
    1715            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1716            0 :                                                          DataDefineEquip::ZnAirLoopEquipType::SingleDuctConstVolFourPipeBeam) ||
    1717            0 :                                                         (state.dataDefineEquipment->AirDistUnit(ADUNum).EquipTypeEnum(EquipNum) ==
    1718              :                                                          DataDefineEquip::ZnAirLoopEquipType::DualDuctVAVOutdoorAir)) {
    1719            0 :                                                         ShowWarningError(state,
    1720            0 :                                                                          format("{}=\"{}\", inappropriate use of Zone secondary recirculation",
    1721              :                                                                                 CurrentModuleObject,
    1722            0 :                                                                                 thisVentilationMechanical.Name));
    1723            0 :                                                         ShowContinueError(state,
    1724              :                                                                           "A zone secondary recirculation fraction is specified for zone served by ");
    1725            0 :                                                         ShowContinueError(state,
    1726            0 :                                                                           format("...terminal unit \"{}\" , that indicates a single path system",
    1727            0 :                                                                                  state.dataDefineEquipment->AirDistUnit(ADUNum).Name));
    1728            0 :                                                         ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1729            0 :                                                         ShowContinueError(state, "...The zone secondary recirculation for that zone was set to 0.0");
    1730            0 :                                                         thisVentMechZone.ZoneSecondaryRecirculation = 0.0;
    1731              :                                                     }
    1732            0 :                                                     goto EquipLoop_exit;
    1733              :                                                 }
    1734              :                                             }
    1735              :                                         }
    1736              :                                     }
    1737              :                                 }
    1738              :                             }
    1739            0 :                         EquipLoop_exit:;
    1740              :                         }
    1741              :                     }
    1742              :                 }
    1743           32 :                 if (thisVentMechZone.ZoneDesignSpecOAObjName.empty()) {
    1744            6 :                     ShowSevereError(
    1745              :                         state,
    1746            6 :                         format("{}=\"{}\", Design Specification Outdoor Air Object Name blank", CurrentModuleObject, thisVentilationMechanical.Name));
    1747            3 :                     ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1748            6 :                     ShowContinueError(state, "This field either needs to be filled in in this object or Sizing:Zone object.");
    1749            9 :                     ShowContinueError(state, "For this run, default values for these fields will be used.");
    1750              :                 }
    1751           32 :                 if (thisVentMechZone.ZoneOAPeopleRate <= 0.0 && thisVentilationMechanical.DCVFlag) {
    1752            9 :                     ShowWarningError(state, format("{}=\"{}\", Zone OA/person rate", CurrentModuleObject, thisVentilationMechanical.Name));
    1753            9 :                     ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1754           18 :                     ShowContinueError(state,
    1755           18 :                                       format("Zone outside air per person rate not set in Design Specification Outdoor Air Object=\"{}\".",
    1756            9 :                                              thisVentMechZone.ZoneDesignSpecOAObjName));
    1757              :                 }
    1758              : 
    1759           32 :                 if (thisVentMechZone.ZoneOAAreaRate < 0.0) {
    1760            0 :                     ShowSevereError(state,
    1761            0 :                                     format("{}=\"{}\", invalid Outdoor Air flow per area", CurrentModuleObject, thisVentilationMechanical.Name));
    1762            0 :                     ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1763            0 :                     ShowContinueError(state,
    1764            0 :                                       format("invalid Outdoor Air flow per area specified in object=\"{}\". Value must be >= 0.0.",
    1765            0 :                                              thisVentMechZone.ZoneDesignSpecOAObjName));
    1766            0 :                     ErrorsFound = true;
    1767              :                 }
    1768           32 :                 if (thisVentMechZone.ZoneOAPeopleRate < 0.0) {
    1769            0 :                     ShowSevereError(state,
    1770            0 :                                     format("{}=\"{}\", invalid Outdoor Air flow per person", CurrentModuleObject, thisVentilationMechanical.Name));
    1771            0 :                     ShowContinueError(state, format("For Zone=\"{}\".", thisVentMechZone.name));
    1772            0 :                     ShowContinueError(state,
    1773            0 :                                       format("invalid Outdoor Air flow per person specified in object \"{}\". Value must be >= 0.0.",
    1774            0 :                                              thisVentMechZone.ZoneDesignSpecOAObjName));
    1775            0 :                     ErrorsFound = true;
    1776              :                 }
    1777              :             }
    1778              :         }
    1779              : 
    1780              :         // Link OA controller object with mechanical ventilation object
    1781           27 :         for (int OAControllerNum = 1; OAControllerNum <= state.dataMixedAir->NumOAControllers; ++OAControllerNum) {
    1782           11 :             state.dataMixedAir->OAController(OAControllerNum).VentMechObjectNum = Util::FindItemInList(
    1783           11 :                 state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName, state.dataMixedAir->VentilationMechanical);
    1784           11 :             if (state.dataMixedAir->OAController(OAControllerNum).VentMechObjectNum == 0 &&
    1785            0 :                 !state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName.empty()) {
    1786            0 :                 ShowSevereError(state,
    1787            0 :                                 format("{}=\"{}\", non-match to Controller:OutdoorAir",
    1788              :                                        CurrentModuleObject,
    1789            0 :                                        state.dataMixedAir->OAController(OAControllerNum).VentilationMechanicalName));
    1790            0 :                 ShowContinueError(
    1791            0 :                     state, format("Invalid specified in Controller:OutdoorAir object = {}", state.dataMixedAir->OAController(OAControllerNum).Name));
    1792            0 :                 ShowContinueError(state,
    1793            0 :                                   format("{} object name must match the {} object name specified in Controller:OutdoorAir.",
    1794              :                                          CurrentModuleObject,
    1795              :                                          CurrentModuleObject));
    1796            0 :                 ErrorsFound = true;
    1797              :             }
    1798              :         }
    1799              : 
    1800              :         // write to .eio file
    1801              :         static constexpr std::string_view Format_700(
    1802              :             "!<Controller:MechanicalVentilation>,Name,Availability Schedule Name,Demand Controlled Ventilation "
    1803              :             "{Yes/No},System Outdoor Air Method,Zone Maximum Outdoor Air Fraction,Number of Zones,Zone Name,DSOA "
    1804              :             "Name,DSZAD Name");
    1805           16 :         print(state.files.eio, "{}\n", Format_700);
    1806           32 :         for (auto const &ventMech : state.dataMixedAir->VentilationMechanical) {
    1807           16 :             print(state.files.eio, " Controller:MechanicalVentilation,{},{},", ventMech.Name, ventMech.availSched ? ventMech.availSched->Name : "");
    1808              : 
    1809           16 :             print(state.files.eio, format("{},", yesNoNames[(int)ventMech.DCVFlag]));
    1810              : 
    1811           16 :             if (ventMech.SystemOAMethod != DataSizing::SysOAMethod::Invalid) {
    1812           16 :                 print(state.files.eio, printSysOAMethod[(int)ventMech.SystemOAMethod]);
    1813              :             } else {
    1814            0 :                 print(state.files.eio, "Invalid/Unknown,");
    1815              :             }
    1816              : 
    1817           16 :             print(state.files.eio, "{:.2R},", ventMech.ZoneMaxOAFraction);
    1818           16 :             print(state.files.eio, "{},", ventMech.NumofVentMechZones);
    1819              : 
    1820           48 :             for (int jZone = 1; jZone <= ventMech.NumofVentMechZones; ++jZone) {
    1821           32 :                 auto &thisVentMechZone = ventMech.VentMechZone(jZone);
    1822           32 :                 if (jZone < ventMech.NumofVentMechZones) {
    1823           16 :                     print(state.files.eio,
    1824              :                           "{},{},{},",
    1825           16 :                           state.dataHeatBal->Zone(thisVentMechZone.zoneNum).Name,
    1826           16 :                           thisVentMechZone.ZoneDesignSpecOAObjName,
    1827           16 :                           thisVentMechZone.ZoneDesignSpecADObjName);
    1828              :                 } else {
    1829           16 :                     print(state.files.eio,
    1830              :                           "{},{},{}\n",
    1831           16 :                           state.dataHeatBal->Zone(thisVentMechZone.zoneNum).Name,
    1832           16 :                           thisVentMechZone.ZoneDesignSpecOAObjName,
    1833           16 :                           thisVentMechZone.ZoneDesignSpecADObjName);
    1834              :                 }
    1835              :             }
    1836           16 :         }
    1837              : 
    1838              :     } // Number of Mechanical Ventilation Objects > 0
    1839              : 
    1840           52 :     AlphArray.deallocate();
    1841           52 :     NumArray.deallocate();
    1842           52 :     lNumericBlanks.deallocate();
    1843           52 :     lAlphaBlanks.deallocate();
    1844           52 :     cAlphaFields.deallocate();
    1845           52 :     cNumericFields.deallocate();
    1846              : 
    1847           52 :     if (ErrorsFound) {
    1848            0 :         ShowFatalError(state, format("{}Errors found when getting {} inputs.", RoutineName, CurrentModuleObject));
    1849              :     }
    1850           52 : }
    1851              : 
    1852           52 : void AllocateOAControllers(EnergyPlusData &state)
    1853              : {
    1854              :     // PURPOSE OF THIS SUBROUTINE:
    1855              :     // Allocate the OA controller arrays which are shared by Controller:OutdoorAir and ZoneHVAC:EnergyRecoveryVentilator:Controller
    1856              : 
    1857           52 :     if (state.dataMixedAir->AllocateOAControllersFlag) {
    1858          104 :         state.dataMixedAir->NumOAControllers =
    1859           52 :             state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObjects[static_cast<int>(CMO::OAController)]);
    1860          104 :         state.dataMixedAir->NumERVControllers =
    1861           52 :             state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObjects[static_cast<int>(CMO::ERVController)]);
    1862           52 :         state.dataMixedAir->NumOAControllers += state.dataMixedAir->NumERVControllers;
    1863           52 :         state.dataMixedAir->OAController.allocate(state.dataMixedAir->NumOAControllers);
    1864           52 :         state.dataMixedAir->OAControllerUniqueNames.reserve(static_cast<unsigned>(state.dataMixedAir->NumOAControllers));
    1865           52 :         state.dataMixedAir->AllocateOAControllersFlag = false;
    1866              :     }
    1867           52 : }
    1868              : 
    1869          124 : void GetOAMixerInputs(EnergyPlusData &state)
    1870              : {
    1871              : 
    1872              :     // SUBROUTINE INFORMATION:
    1873              :     //       AUTHOR         Fred Buhl
    1874              :     //       DATE WRITTEN   Oct 1998
    1875              : 
    1876              :     // PURPOSE OF THIS SUBROUTINE
    1877              :     // Input the OAMixer data and store it in the OAMixer array.
    1878              : 
    1879              :     // METHODOLOGY EMPLOYED:
    1880              :     // Use the Get routines from the InputProcessor module.
    1881              : 
    1882              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1883              :     static constexpr std::string_view RoutineName("GetOAMixerInputs: "); // include trailing blank space
    1884              : 
    1885              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1886              : 
    1887              :     int NumNums;                   // Number of REAL(r64) numbers returned by GetObjectItem
    1888              :     int NumAlphas;                 // Number of alphanumerics returned by GetObjectItem
    1889              :     int NumArg;                    // Number of arguments from GetObjectDefMaxArgs call
    1890          124 :     Array1D<Real64> NumArray;      // array that holds numeric input values
    1891          124 :     Array1D_string AlphArray;      // array that holds alpha input values
    1892          124 :     Array1D_string cAlphaFields;   // Alpha field names
    1893          124 :     Array1D_string cNumericFields; // Numeric field names
    1894          124 :     Array1D_bool lAlphaBlanks;     // Logical array, alpha field input BLANK = .TRUE.
    1895          124 :     Array1D_bool lNumericBlanks;   // Logical array, numeric field input BLANK = .TRUE.
    1896          124 :     bool ErrorsFound(false);
    1897              : 
    1898          124 :     if (!state.dataMixedAir->GetOAMixerInputFlag) {
    1899            0 :         return;
    1900              :     }
    1901              : 
    1902          248 :     state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
    1903          124 :         state, CurrentModuleObjects[static_cast<int>(CMO::OAMixer)], NumArg, NumAlphas, NumNums);
    1904              : 
    1905          124 :     AlphArray.allocate(NumAlphas);
    1906          124 :     NumArray.dimension(NumNums, 0.0);
    1907          124 :     lNumericBlanks.dimension(NumNums, true);
    1908          124 :     lAlphaBlanks.dimension(NumAlphas, true);
    1909          124 :     cAlphaFields.allocate(NumAlphas);
    1910          124 :     cNumericFields.allocate(NumNums);
    1911              : 
    1912          124 :     std::string_view const CurrentModuleObject = CurrentModuleObjects[static_cast<int>(CMO::OAMixer)];
    1913              : 
    1914          124 :     state.dataMixedAir->NumOAMixers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
    1915              : 
    1916          124 :     if (state.dataMixedAir->NumOAMixers > 0) {
    1917              : 
    1918           99 :         state.dataMixedAir->OAMixer.allocate(state.dataMixedAir->NumOAMixers);
    1919              :         int IOStat;
    1920              : 
    1921          244 :         for (int OutAirNum = 1; OutAirNum <= state.dataMixedAir->NumOAMixers; ++OutAirNum) {
    1922          145 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    1923              :                                                                      CurrentModuleObject,
    1924              :                                                                      OutAirNum,
    1925              :                                                                      AlphArray,
    1926              :                                                                      NumAlphas,
    1927              :                                                                      NumArray,
    1928              :                                                                      NumNums,
    1929              :                                                                      IOStat,
    1930              :                                                                      lNumericBlanks,
    1931              :                                                                      lAlphaBlanks,
    1932              :                                                                      cAlphaFields,
    1933              :                                                                      cNumericFields);
    1934              :             // no need to check if AlphaArray(1) is empty since Json will catch missing required fields
    1935          145 :             state.dataMixedAir->OAMixer(OutAirNum).Name = AlphArray(1);
    1936          290 :             state.dataMixedAir->OAMixer(OutAirNum).MixNode = NodeInputManager::GetOnlySingleNode(state,
    1937          145 :                                                                                                  AlphArray(2),
    1938              :                                                                                                  ErrorsFound,
    1939              :                                                                                                  DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
    1940          145 :                                                                                                  AlphArray(1),
    1941              :                                                                                                  DataLoopNode::NodeFluidType::Air,
    1942              :                                                                                                  DataLoopNode::ConnectionType::Outlet,
    1943              :                                                                                                  NodeInputManager::CompFluidStream::Primary,
    1944              :                                                                                                  ObjectIsNotParent);
    1945              :             //  Set connection type to 'Inlet', because this is not necessarily directly from
    1946              :             //  outside air.  Outside Air Inlet Node List will set the connection to outside air
    1947          145 :             state.dataMixedAir->OAMixer(OutAirNum).InletNode =
    1948          290 :                 NodeInputManager::GetOnlySingleNode(state,
    1949          145 :                                                     AlphArray(3),
    1950              :                                                     ErrorsFound,
    1951              :                                                     DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
    1952          145 :                                                     AlphArray(1),
    1953              :                                                     DataLoopNode::NodeFluidType::Air,
    1954              :                                                     DataLoopNode::ConnectionType::Inlet,
    1955              :                                                     NodeInputManager::CompFluidStream::Primary,
    1956              :                                                     ObjectIsNotParent);
    1957          290 :             state.dataMixedAir->OAMixer(OutAirNum).RelNode = NodeInputManager::GetOnlySingleNode(state,
    1958          145 :                                                                                                  AlphArray(4),
    1959              :                                                                                                  ErrorsFound,
    1960              :                                                                                                  DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
    1961          145 :                                                                                                  AlphArray(1),
    1962              :                                                                                                  DataLoopNode::NodeFluidType::Air,
    1963              :                                                                                                  DataLoopNode::ConnectionType::ReliefAir,
    1964              :                                                                                                  NodeInputManager::CompFluidStream::Primary,
    1965              :                                                                                                  ObjectIsNotParent);
    1966          290 :             state.dataMixedAir->OAMixer(OutAirNum).RetNode = NodeInputManager::GetOnlySingleNode(state,
    1967          145 :                                                                                                  AlphArray(5),
    1968              :                                                                                                  ErrorsFound,
    1969              :                                                                                                  DataLoopNode::ConnectionObjectType::OutdoorAirMixer,
    1970          145 :                                                                                                  AlphArray(1),
    1971              :                                                                                                  DataLoopNode::NodeFluidType::Air,
    1972              :                                                                                                  DataLoopNode::ConnectionType::Inlet,
    1973              :                                                                                                  NodeInputManager::CompFluidStream::Primary,
    1974              :                                                                                                  ObjectIsNotParent);
    1975              :             // Check for dupes in the four nodes.
    1976          145 :             if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).InletNode) {
    1977            0 :                 ShowSevereError(state,
    1978            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    1979              :                                        CurrentModuleObject,
    1980            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    1981              :                                        cAlphaFields(3),
    1982            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).InletNode),
    1983              :                                        cAlphaFields(2)));
    1984            0 :                 ErrorsFound = true;
    1985          145 :             } else if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).RelNode) {
    1986            0 :                 ShowSevereError(state,
    1987            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    1988              :                                        CurrentModuleObject,
    1989            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    1990              :                                        cAlphaFields(4),
    1991            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RelNode),
    1992              :                                        cAlphaFields(2)));
    1993            0 :                 ErrorsFound = true;
    1994          145 :             } else if (state.dataMixedAir->OAMixer(OutAirNum).MixNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
    1995            0 :                 ShowSevereError(state,
    1996            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    1997              :                                        CurrentModuleObject,
    1998            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    1999              :                                        cAlphaFields(5),
    2000            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
    2001              :                                        cAlphaFields(2)));
    2002            0 :                 ErrorsFound = true;
    2003              :             }
    2004              : 
    2005          145 :             if (state.dataMixedAir->OAMixer(OutAirNum).InletNode == state.dataMixedAir->OAMixer(OutAirNum).RelNode) {
    2006            0 :                 ShowSevereError(state,
    2007            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    2008              :                                        CurrentModuleObject,
    2009            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    2010              :                                        cAlphaFields(4),
    2011            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RelNode),
    2012              :                                        cAlphaFields(3)));
    2013            0 :                 ErrorsFound = true;
    2014          145 :             } else if (state.dataMixedAir->OAMixer(OutAirNum).InletNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
    2015            0 :                 ShowSevereError(state,
    2016            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    2017              :                                        CurrentModuleObject,
    2018            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    2019              :                                        cAlphaFields(5),
    2020            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
    2021              :                                        cAlphaFields(3)));
    2022            0 :                 ErrorsFound = true;
    2023              :             }
    2024              : 
    2025          145 :             if (state.dataMixedAir->OAMixer(OutAirNum).RelNode == state.dataMixedAir->OAMixer(OutAirNum).RetNode) {
    2026            0 :                 ShowSevereError(state,
    2027            0 :                                 format("{} = {} {} = {} duplicates the {}.",
    2028              :                                        CurrentModuleObject,
    2029            0 :                                        state.dataMixedAir->OAMixer(OutAirNum).Name,
    2030              :                                        cAlphaFields(5),
    2031            0 :                                        state.dataLoopNodes->NodeID(state.dataMixedAir->OAMixer(OutAirNum).RetNode),
    2032              :                                        cAlphaFields(4)));
    2033            0 :                 ErrorsFound = true;
    2034              :             }
    2035          435 :             BranchNodeConnections::TestCompSet(
    2036          145 :                 state, CurrentModuleObject, state.dataMixedAir->OAMixer(OutAirNum).Name, AlphArray(3), AlphArray(2), "Air Nodes");
    2037              :         }
    2038              :     }
    2039              : 
    2040          124 :     if (ErrorsFound) {
    2041            0 :         ShowFatalError(state, format("{}Errors found in getting {}", RoutineName, CurrentModuleObject));
    2042              :     }
    2043              : 
    2044          124 :     state.dataMixedAir->GetOAMixerInputFlag = false;
    2045          124 : }
    2046              : 
    2047           56 : void ProcessOAControllerInputs(EnergyPlusData &state,
    2048              :                                std::string_view const CurrentModuleObject,
    2049              :                                int const OutAirNum,
    2050              :                                Array1D_string const &AlphArray,
    2051              :                                int const NumAlphas,
    2052              :                                Array1D<Real64> const &NumArray,
    2053              :                                int const NumNums,
    2054              :                                Array1D_bool const &lNumericBlanks, // Unused
    2055              :                                Array1D_bool const &lAlphaBlanks,
    2056              :                                Array1D_string const &cAlphaFields,
    2057              :                                Array1D_string const &cNumericFields, // Unused
    2058              :                                bool &ErrorsFound                     // If errors found in input
    2059              : )
    2060              : {
    2061              : 
    2062              :     // SUBROUTINE INFORMATION:
    2063              :     //       AUTHOR         Fred Buhl
    2064              :     //       DATE WRITTEN   Oct 1998
    2065              :     //       MODIFIED       Shirey/Raustad FSEC, June 2003, Jan 2004
    2066              :     //                      Mangesh Basarkar, 06/2011: Getting zone OA specifications from Design Specification Object
    2067              :     //                      Tianzhen Hong, 3/2012: getting zone air distribution effectiveness and secondary recirculation
    2068              :     //                       from DesignSpecification:ZoneAirDistribution objects
    2069              :     //       RE-ENGINEERED  MJW: Split out processing controller:outdoorair input to facilitate unit testing, Feb 2015
    2070              : 
    2071              :     // PURPOSE OF THIS SUBROUTINE
    2072              :     // Input the OAController data and store it in the OAController array.
    2073              : 
    2074              :     // SUBROUTINE PARAMETER DEFINITIONS:
    2075              :     static constexpr std::string_view RoutineName("GetOAControllerInputs: "); // include trailing blank space
    2076              :     static constexpr std::string_view routineName = "GetOAControllerInputs";
    2077              : 
    2078           56 :     ErrorObjectHeader eoh{routineName, CurrentModuleObject, AlphArray(1)};
    2079              : 
    2080           56 :     state.dataMixedAir->OAController(OutAirNum).Name = AlphArray(1);
    2081           56 :     state.dataMixedAir->OAController(OutAirNum).ControllerType = MixedAirControllerType::ControllerOutsideAir;
    2082           56 :     state.dataMixedAir->OAController(OutAirNum).MaxOA = NumArray(2);
    2083           56 :     state.dataMixedAir->OAController(OutAirNum).MinOA = NumArray(1);
    2084           56 :     state.dataMixedAir->OAController(OutAirNum).MixNode =
    2085          112 :         NodeInputManager::GetOnlySingleNode(state,
    2086           56 :                                             AlphArray(4),
    2087              :                                             ErrorsFound,
    2088              :                                             DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
    2089           56 :                                             AlphArray(1),
    2090              :                                             DataLoopNode::NodeFluidType::Air,
    2091              :                                             DataLoopNode::ConnectionType::Sensor,
    2092              :                                             NodeInputManager::CompFluidStream::Primary,
    2093              :                                             ObjectIsNotParent);
    2094          112 :     state.dataMixedAir->OAController(OutAirNum).OANode = NodeInputManager::GetOnlySingleNode(state,
    2095           56 :                                                                                              AlphArray(5),
    2096              :                                                                                              ErrorsFound,
    2097              :                                                                                              DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
    2098           56 :                                                                                              AlphArray(1),
    2099              :                                                                                              DataLoopNode::NodeFluidType::Air,
    2100              :                                                                                              DataLoopNode::ConnectionType::Actuator,
    2101              :                                                                                              NodeInputManager::CompFluidStream::Primary,
    2102              :                                                                                              ObjectIsNotParent);
    2103           56 :     if (!OutAirNodeManager::CheckOutAirNodeNumber(state, state.dataMixedAir->OAController(OutAirNum).OANode)) {
    2104           18 :         ShowWarningError(state,
    2105           18 :                          format("{}=\"{}\": {}=\"{}\" is not an OutdoorAir:Node.", CurrentModuleObject, AlphArray(1), cAlphaFields(5), AlphArray(5)));
    2106           27 :         ShowContinueError(state, "Confirm that this is the intended source for the outdoor air stream.");
    2107              :     }
    2108           56 :     if (Util::SameString(AlphArray(6), "NoEconomizer")) {
    2109           33 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::NoEconomizer;
    2110           23 :     } else if (Util::SameString(AlphArray(6), "FixedDryBulb")) {
    2111            3 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedDryBulb;
    2112           20 :     } else if (Util::SameString(AlphArray(6), "FixedEnthalpy")) {
    2113            0 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedEnthalpy;
    2114           20 :     } else if (Util::SameString(AlphArray(6), "FixedDewPointAndDryBulb")) {
    2115            0 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::FixedDewPointAndDryBulb;
    2116           20 :     } else if (Util::SameString(AlphArray(6), "DifferentialDryBulb")) {
    2117           18 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialDryBulb;
    2118            2 :     } else if (Util::SameString(AlphArray(6), "DifferentialEnthalpy")) {
    2119            2 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialEnthalpy;
    2120            0 :     } else if (Util::SameString(AlphArray(6), "DifferentialDryBulbAndEnthalpy")) {
    2121            0 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::DifferentialDryBulbAndEnthalpy;
    2122            0 :     } else if (Util::SameString(AlphArray(6), "ElectronicEnthalpy")) {
    2123            0 :         state.dataMixedAir->OAController(OutAirNum).Econo = EconoOp::ElectronicEnthalpy;
    2124              :     } else {
    2125            0 :         ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(6), AlphArray(6)));
    2126            0 :         ErrorsFound = true;
    2127              :     }
    2128              :     // Bypass choice - Added by Amit for new feature implementation
    2129           56 :     if (Util::SameString(AlphArray(7), "ModulateFlow")) {
    2130           48 :         state.dataMixedAir->OAController(OutAirNum).EconBypass = false;
    2131            8 :     } else if (Util::SameString(AlphArray(7), "MinimumFlowWithBypass")) {
    2132            8 :         state.dataMixedAir->OAController(OutAirNum).EconBypass = true;
    2133              :     } else {
    2134            0 :         ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(7), AlphArray(7)));
    2135            0 :         ErrorsFound = true;
    2136              :     }
    2137              : 
    2138           56 :     if (Util::SameString(AlphArray(9), "NoLockout")) {
    2139           55 :         state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::NoLockoutPossible;
    2140            1 :     } else if (Util::SameString(AlphArray(9), "LockoutWithHeating")) {
    2141            1 :         state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::LockoutWithHeatingPossible;
    2142            0 :     } else if (Util::SameString(AlphArray(9), "LockoutWithCompressor")) {
    2143            0 :         state.dataMixedAir->OAController(OutAirNum).Lockout = LockoutType::LockoutWithCompressorPossible;
    2144              :     } else {
    2145            0 :         ShowSevereError(state, format("{}=\"{}\" invalid {}=\"{}\" value.", CurrentModuleObject, AlphArray(1), cAlphaFields(9), AlphArray(9)));
    2146            0 :         ErrorsFound = true;
    2147              :     }
    2148           56 :     if (Util::SameString(AlphArray(10), "FixedMinimum")) {
    2149           34 :         state.dataMixedAir->OAController(OutAirNum).FixedMin = true;
    2150              :     } else {
    2151           22 :         state.dataMixedAir->OAController(OutAirNum).FixedMin = false;
    2152              :     }
    2153           56 :     if (lNumericBlanks(3)) {
    2154           37 :         state.dataMixedAir->OAController(OutAirNum).TempLim = HVAC::BlankNumeric;
    2155              :     } else {
    2156           19 :         state.dataMixedAir->OAController(OutAirNum).TempLim = NumArray(3);
    2157              :     }
    2158              : 
    2159           56 :     if (lNumericBlanks(4)) {
    2160           45 :         state.dataMixedAir->OAController(OutAirNum).EnthLim = HVAC::BlankNumeric;
    2161              :     } else {
    2162           11 :         state.dataMixedAir->OAController(OutAirNum).EnthLim = NumArray(4);
    2163              :     }
    2164           56 :     if (lNumericBlanks(5)) {
    2165           54 :         state.dataMixedAir->OAController(OutAirNum).DPTempLim = HVAC::BlankNumeric;
    2166              :     } else {
    2167            2 :         state.dataMixedAir->OAController(OutAirNum).DPTempLim = NumArray(5);
    2168              :     }
    2169              : 
    2170           56 :     if (lNumericBlanks(6)) {
    2171           28 :         state.dataMixedAir->OAController(OutAirNum).TempLowLim = HVAC::BlankNumeric;
    2172              :     } else {
    2173           28 :         state.dataMixedAir->OAController(OutAirNum).TempLowLim = NumArray(6);
    2174              :     }
    2175              : 
    2176           56 :     if (!lAlphaBlanks(8)) {
    2177            0 :         state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr = Curve::GetCurveIndex(state, AlphArray(8)); // convert curve name to number
    2178            0 :         if (state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr == 0) {
    2179            0 :             ShowSevereError(state,
    2180            0 :                             format("{}=\"{}\" invalid {}=\"{}\" not found.", CurrentModuleObject, AlphArray(1), cAlphaFields(8), AlphArray(8)));
    2181            0 :             ErrorsFound = true;
    2182              :         } else {
    2183              :             // Verify Curve Object, only legal types are Quadratic and Cubic
    2184            0 :             ErrorsFound |= Curve::CheckCurveDims(state,
    2185            0 :                                                  state.dataMixedAir->OAController(OutAirNum).EnthalpyCurvePtr, // Curve index
    2186              :                                                  {1},                                                          // Valid dimensions
    2187              :                                                  RoutineName,                                                  // Routine name
    2188              :                                                  CurrentModuleObject,                                          // Object Type
    2189            0 :                                                  state.dataMixedAir->OAController(OutAirNum).Name,             // Object Name
    2190            0 :                                                  cAlphaFields(8));                                             // Field Name
    2191              :         }
    2192              :     }
    2193              : 
    2194           56 :     state.dataMixedAir->OAController(OutAirNum).RelNode =
    2195          112 :         NodeInputManager::GetOnlySingleNode(state,
    2196           56 :                                             AlphArray(2),
    2197              :                                             ErrorsFound,
    2198              :                                             DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
    2199           56 :                                             AlphArray(1),
    2200              :                                             DataLoopNode::NodeFluidType::Air,
    2201              :                                             DataLoopNode::ConnectionType::Actuator,
    2202              :                                             NodeInputManager::CompFluidStream::Primary,
    2203              :                                             ObjectIsNotParent);
    2204           56 :     state.dataMixedAir->OAController(OutAirNum).RetNode =
    2205          112 :         NodeInputManager::GetOnlySingleNode(state,
    2206           56 :                                             AlphArray(3),
    2207              :                                             ErrorsFound,
    2208              :                                             DataLoopNode::ConnectionObjectType::ControllerOutdoorAir,
    2209           56 :                                             AlphArray(1),
    2210              :                                             DataLoopNode::NodeFluidType::Air,
    2211              :                                             DataLoopNode::ConnectionType::Sensor,
    2212              :                                             NodeInputManager::CompFluidStream::Primary,
    2213              :                                             ObjectIsNotParent);
    2214              : 
    2215           56 :     if (lAlphaBlanks(11)) {
    2216           21 :     } else if ((state.dataMixedAir->OAController(OutAirNum).minOASched = Sched::GetSchedule(state, AlphArray(11))) == nullptr) {
    2217            0 :         ShowSevereItemNotFound(state, eoh, cAlphaFields(11), AlphArray(11));
    2218            0 :         ErrorsFound = true;
    2219              :     }
    2220              : 
    2221              :     // Changed by Amit for new feature implementation
    2222           56 :     if (lAlphaBlanks(12)) {
    2223            0 :     } else if ((state.dataMixedAir->OAController(OutAirNum).minOAflowSched = Sched::GetSchedule(state, AlphArray(12))) == nullptr) {
    2224            0 :         ShowSevereItemNotFound(state, eoh, cAlphaFields(12), AlphArray(12));
    2225            0 :         ErrorsFound = true;
    2226              :     }
    2227              : 
    2228           56 :     if (lAlphaBlanks(13)) {
    2229            0 :     } else if ((state.dataMixedAir->OAController(OutAirNum).maxOAflowSched = Sched::GetSchedule(state, AlphArray(13))) == nullptr) {
    2230            0 :         ShowSevereItemNotFound(state, eoh, cAlphaFields(13), AlphArray(13));
    2231            0 :         ErrorsFound = true;
    2232              :     }
    2233           56 :     state.dataMixedAir->OAController(OutAirNum).VentilationMechanicalName = AlphArray(14);
    2234              : 
    2235              :     //   Check for a time of day economizer control schedule
    2236           56 :     state.dataMixedAir->OAController(OutAirNum).economizerOASched = Sched::GetSchedule(state, AlphArray(15));
    2237              : 
    2238              :     //   High humidity control option can be used with any economizer flag
    2239           56 :     if (Util::SameString(AlphArray(16), "Yes")) {
    2240              : 
    2241            2 :         state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum = Util::FindItemInList(AlphArray(17), state.dataHeatBal->Zone);
    2242              : 
    2243              :         // Get the node number for the zone with the humidistat
    2244            2 :         if (state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum > 0) {
    2245            2 :             bool AirNodeFound = false;
    2246            2 :             bool AirLoopFound = false;
    2247            2 :             bool OASysFound = false;
    2248            4 :             for (int ControlledZoneNum = 1; ControlledZoneNum <= state.dataGlobal->NumOfZones; ++ControlledZoneNum) {
    2249            2 :                 if (ControlledZoneNum != state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum) {
    2250            0 :                     continue;
    2251              :                 }
    2252              :                 //           Find the controlled zone number for the specified humidistat location
    2253            2 :                 state.dataMixedAir->OAController(OutAirNum).NodeNumofHumidistatZone =
    2254            2 :                     state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
    2255              :                 //           Determine which OA System uses this OA Controller
    2256            2 :                 int OASysIndex = 0;
    2257            2 :                 for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
    2258            2 :                     for (int OAControllerNum = 1; OAControllerNum <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers; ++OAControllerNum) {
    2259            4 :                         if (!Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerType(OAControllerNum), CurrentModuleObject) ||
    2260            2 :                             !Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerNum),
    2261            2 :                                               state.dataMixedAir->OAController(OutAirNum).Name)) {
    2262            0 :                             continue;
    2263              :                         }
    2264            2 :                         OASysIndex = OASysNum;
    2265            2 :                         OASysFound = true;
    2266            2 :                         break;
    2267              :                     }
    2268            2 :                     if (OASysFound) {
    2269            2 :                         break;
    2270              :                     }
    2271              :                 }
    2272              :                 //           Determine if controller is on air loop served by the humidistat location specified
    2273            4 :                 for (int zoneInNode = 1; zoneInNode <= state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).NumInletNodes; ++zoneInNode) {
    2274            2 :                     int AirLoopNumber = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).InletNodeAirLoopNum(zoneInNode);
    2275            2 :                     if (AirLoopNumber > 0 && OASysIndex > 0) {
    2276            2 :                         for (int BranchNum = 1; BranchNum <= state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).NumBranches; ++BranchNum) {
    2277            2 :                             for (int CompNum = 1;
    2278            2 :                                  CompNum <= state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).TotalComponents;
    2279              :                                  ++CompNum) {
    2280            2 :                                 if (!Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).Comp(CompNum).Name,
    2281            6 :                                                       state.dataAirLoop->OutsideAirSys(OASysIndex).Name) ||
    2282            6 :                                     !Util::SameString(
    2283            2 :                                         state.dataAirSystemsData->PrimaryAirSystems(AirLoopNumber).Branch(BranchNum).Comp(CompNum).TypeOf,
    2284              :                                         "AirLoopHVAC:OutdoorAirSystem")) {
    2285            0 :                                     continue;
    2286              :                                 }
    2287            2 :                                 AirLoopFound = true;
    2288            2 :                                 break;
    2289              :                             }
    2290            2 :                             if (AirLoopFound) {
    2291            2 :                                 break;
    2292              :                             }
    2293              :                         }
    2294            2 :                         for (int HStatZoneNum = 1; HStatZoneNum <= state.dataZoneCtrls->NumHumidityControlZones; ++HStatZoneNum) {
    2295            2 :                             if (state.dataZoneCtrls->HumidityControlZone(HStatZoneNum).ActualZoneNum !=
    2296            2 :                                 state.dataMixedAir->OAController(OutAirNum).HumidistatZoneNum) {
    2297            0 :                                 continue;
    2298              :                             }
    2299            2 :                             AirNodeFound = true;
    2300            2 :                             break;
    2301              :                         }
    2302            2 :                     } else {
    2303            0 :                         if (OASysIndex == 0) {
    2304            0 :                             ShowSevereError(
    2305              :                                 state,
    2306            0 :                                 format("Did not find an AirLoopHVAC:OutdoorAirSystem for {} = \"{}\"",
    2307            0 :                                        MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
    2308            0 :                                        state.dataMixedAir->OAController(OutAirNum).Name));
    2309            0 :                             ErrorsFound = true;
    2310              :                         }
    2311              :                     }
    2312              :                 }
    2313              :             }
    2314            2 :             if (!AirNodeFound) {
    2315            0 :                 ShowSevereError(state,
    2316            0 :                                 format("Did not find Air Node (Zone with Humidistat), {} = \"{}\"",
    2317            0 :                                        MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
    2318            0 :                                        state.dataMixedAir->OAController(OutAirNum).Name));
    2319            0 :                 ShowContinueError(state, format("Specified {} = {}", cAlphaFields(17), AlphArray(17)));
    2320            0 :                 ShowContinueError(state,
    2321              :                                   "Both a ZoneHVAC:EquipmentConnections object and a ZoneControl:Humidistat object must be specified for this zone.");
    2322            0 :                 ErrorsFound = true;
    2323              :             }
    2324            2 :             if (!AirLoopFound) {
    2325            0 :                 ShowSevereError(state,
    2326            0 :                                 format("Did not find correct Primary Air Loop for {} = \"{}\"",
    2327            0 :                                        MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
    2328            0 :                                        state.dataMixedAir->OAController(OutAirNum).Name));
    2329            0 :                 ShowContinueError(state, format("{} = {} is not served by this Primary Air Loop equipment.", cAlphaFields(17), AlphArray(17)));
    2330            0 :                 ErrorsFound = true;
    2331              :             }
    2332              :         } else {
    2333            0 :             ShowSevereError(state,
    2334            0 :                             format("Did not find Air Node (Zone with Humidistat), {} = \"{}\"",
    2335            0 :                                    MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
    2336            0 :                                    state.dataMixedAir->OAController(OutAirNum).Name));
    2337            0 :             ShowContinueError(state, format("Specified {} = {}", cAlphaFields(17), AlphArray(17)));
    2338            0 :             ShowContinueError(state,
    2339              :                               "Both a ZoneHVAC:EquipmentConnections object and a ZoneControl:Humidistat object must be specified for this zone.");
    2340            0 :             ErrorsFound = true;
    2341              :         }
    2342              : 
    2343            2 :         state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio = NumArray(7);
    2344            2 :         if (state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio <= 0.0 && NumNums > 6) {
    2345            0 :             ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2346            0 :             ShowContinueError(state, format(" {} must be greater than 0.", cNumericFields(7)));
    2347            0 :             ShowContinueError(state, format(" {} is reset to 1 and the simulation continues.", cNumericFields(7)));
    2348            0 :             state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio = 1.0;
    2349              :         }
    2350              : 
    2351            2 :         if (Util::SameString(AlphArray(16), "Yes") && state.dataMixedAir->OAController(OutAirNum).FixedMin) {
    2352            0 :             if (state.dataMixedAir->OAController(OutAirNum).MaxOA > 0.0 && state.dataMixedAir->OAController(OutAirNum).MinOA != AutoSize) {
    2353            0 :                 Real64 OAFlowRatio = state.dataMixedAir->OAController(OutAirNum).MinOA / state.dataMixedAir->OAController(OutAirNum).MaxOA;
    2354            0 :                 if (state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio < OAFlowRatio) {
    2355            0 :                     ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2356            0 :                     ShowContinueError(state, "... A fixed minimum outside air flow rate and high humidity control have been specified.");
    2357            0 :                     ShowContinueError(
    2358              :                         state,
    2359            0 :                         format("... The {} is less than the ratio of the outside air controllers minimum to maximum outside air flow rate.",
    2360              :                                cNumericFields(7)));
    2361            0 :                     ShowContinueError(
    2362            0 :                         state, format("... Controller {} = {:.4T} m3/s.", cNumericFields(1), state.dataMixedAir->OAController(OutAirNum).MinOA));
    2363            0 :                     ShowContinueError(
    2364            0 :                         state, format("... Controller {} = {:.4T} m3/s.", cNumericFields(2), state.dataMixedAir->OAController(OutAirNum).MaxOA));
    2365            0 :                     ShowContinueError(state, format("... Controller minimum to maximum flow ratio = {:.4T}.", OAFlowRatio));
    2366            0 :                     ShowContinueError(state,
    2367            0 :                                       format("... {} = {:.4T}.", cNumericFields(7), state.dataMixedAir->OAController(OutAirNum).HighRHOAFlowRatio));
    2368              :                 }
    2369              :             }
    2370              :         }
    2371              : 
    2372            2 :         if (NumAlphas >= 18) {
    2373            1 :             if (Util::SameString(AlphArray(18), "Yes")) {
    2374            1 :                 state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = false;
    2375            0 :             } else if (Util::SameString(AlphArray(18), "No")) {
    2376            0 :                 state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = true;
    2377              :             } else {
    2378            0 :                 ShowSevereError(state,
    2379            0 :                                 format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2380            0 :                 ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
    2381            0 :                 ErrorsFound = true;
    2382              :             }
    2383              :         } else {
    2384            1 :             if (state.dataMixedAir->OAController(OutAirNum).Econo == EconoOp::NoEconomizer) {
    2385            0 :                 state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = true;
    2386              :             } else {
    2387            1 :                 state.dataMixedAir->OAController(OutAirNum).ModifyDuringHighOAMoisture = false;
    2388            2 :                 ShowWarningError(state,
    2389            2 :                                  format("{} \"{}\", missing field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2390            1 :                 ShowContinueError(state, format("...{} will default to Yes when {}= \"Yes\"", cAlphaFields(18), cAlphaFields(16)));
    2391              :             }
    2392              :         }
    2393              : 
    2394           54 :     } else if (Util::SameString(AlphArray(16), "No") || lAlphaBlanks(16)) {
    2395           54 :         if (NumAlphas >= 18) {
    2396           18 :             if (!Util::SameString(AlphArray(18), "Yes") && !Util::SameString(AlphArray(18), "No")) {
    2397            0 :                 ShowSevereError(state,
    2398            0 :                                 format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2399            0 :                 ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
    2400            0 :                 ErrorsFound = true;
    2401              :             }
    2402              :         }
    2403              :     } else { // Invalid field 16
    2404            0 :         ShowSevereError(state, format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2405            0 :         ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(16), AlphArray(16)));
    2406            0 :         ErrorsFound = true;
    2407            0 :         if (NumAlphas >= 18) {
    2408            0 :             if (!Util::SameString(AlphArray(18), "Yes") && !Util::SameString(AlphArray(18), "No")) {
    2409            0 :                 ShowSevereError(state,
    2410            0 :                                 format("{} \"{}\", invalid field value", CurrentModuleObject, state.dataMixedAir->OAController(OutAirNum).Name));
    2411            0 :                 ShowContinueError(state, format("...{}=\"{}\" - valid values are \"Yes\" or \"No\".", cAlphaFields(18), AlphArray(18)));
    2412            0 :                 ErrorsFound = true;
    2413              :             }
    2414              :         }
    2415              :     }
    2416              : 
    2417           56 :     if (NumAlphas > 18) {
    2418           14 :         if (!lAlphaBlanks(19)) {
    2419           14 :             if (Util::SameString(AlphArray(19), "BypassWhenWithinEconomizerLimits")) {
    2420            6 :                 state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenWithinEconomizerLimits;
    2421            8 :             } else if (Util::SameString(AlphArray(19), "BypassWhenOAFlowGreaterThanMinimum")) {
    2422            8 :                 state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenOAFlowGreaterThanMinimum;
    2423              :             } else {
    2424            0 :                 ShowWarningError(state, format("{}=\"{}\" invalid {}=\"{}\".", CurrentModuleObject, AlphArray(1), cAlphaFields(19), AlphArray(19)));
    2425            0 :                 ShowContinueError(state, "...assuming \"BypassWhenWithinEconomizerLimits\" and the simulation continues.");
    2426            0 :                 state.dataMixedAir->OAController(OutAirNum).HeatRecoveryBypassControlType = HVAC::BypassWhenWithinEconomizerLimits;
    2427              :             }
    2428              :         }
    2429              :     }
    2430              : 
    2431           56 :     if (NumAlphas > 19) {
    2432            0 :         if (!lAlphaBlanks(20)) {
    2433            0 :             if (Util::SameString(AlphArray(20), "EconomizerFirst")) {
    2434            0 :                 state.dataMixedAir->OAController(OutAirNum).EconomizerStagingType = HVAC::EconomizerStagingType::EconomizerFirst;
    2435              :             } else {
    2436            0 :                 state.dataMixedAir->OAController(OutAirNum).EconomizerStagingType = HVAC::EconomizerStagingType::InterlockedWithMechanicalCooling;
    2437              :             }
    2438              :         }
    2439              :     }
    2440              : 
    2441           56 :     if (Util::SameString(AlphArray(16), "Yes") && state.dataMixedAir->OAController(OutAirNum).Econo == EconoOp::NoEconomizer) {
    2442            0 :         ShowWarningError(state,
    2443            0 :                          format("{} \"{}\"",
    2444            0 :                                 MixedAirControllerTypeNames[static_cast<int>(state.dataMixedAir->OAController(OutAirNum).ControllerType)],
    2445            0 :                                 state.dataMixedAir->OAController(OutAirNum).Name));
    2446            0 :         ShowContinueError(state, format("...Economizer operation must be enabled when {} is set to YES.", cAlphaFields(16)));
    2447            0 :         ShowContinueError(state, "...The high humidity control option will be disabled and the simulation continues.");
    2448              :     }
    2449              : 
    2450           56 :     state.dataMixedAir->OAController(OutAirNum).MixedAirSPMNum =
    2451           56 :         SetPointManager::GetMixedAirNumWithCoilFreezingCheck(state, state.dataMixedAir->OAController(OutAirNum).MixNode);
    2452           56 : }
    2453              : 
    2454              : // End of Get Input subroutines for the Module
    2455              : //******************************************************************************
    2456              : 
    2457              : // Beginning Initialization Section of the Module
    2458              : //******************************************************************************
    2459              : 
    2460        92985 : void InitOutsideAirSys(EnergyPlusData &state, int const(OASysNum), int const AirLoopNum)
    2461              : {
    2462              : 
    2463              :     // SUBROUTINE INFORMATION:
    2464              :     //       AUTHOR         Fred Buhl
    2465              :     //       DATE WRITTEN   Oct 1998
    2466              : 
    2467              :     // PURPOSE OF THIS SUBROUTINE
    2468              :     // Initialize the OutsideAirSys data structure
    2469              : 
    2470        92985 :     if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum > -1) {
    2471        14568 :         return;
    2472              :     }
    2473              : 
    2474        78417 :     if (state.dataMixedAir->initOASysFlag(OASysNum)) {
    2475           25 :         state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum = OASysNum;
    2476           25 :         state.dataMixedAir->initOASysFlag(OASysNum) = false;
    2477              :     }
    2478              : }
    2479              : 
    2480        78423 : void InitOAController(EnergyPlusData &state, int const OAControllerNum, bool const FirstHVACIteration, int const AirLoopNum)
    2481              : {
    2482              : 
    2483              :     // SUBROUTINE INFORMATION:
    2484              :     //       AUTHOR         Fred Buhl
    2485              :     //       DATE WRITTEN   Oct 1998
    2486              :     //       MODIFIED       Shirey/Raustad FSEC, June/Aug 2003, Feb 2004
    2487              :     //                      Tianzhen Hong, Feb 2009 for DCV
    2488              :     //                      Tianzhen Hong, Aug 2013 for economizer faults
    2489              : 
    2490              :     // PURPOSE OF THIS SUBROUTINE
    2491              :     // Initialize the OAController data structure with input node data
    2492              : 
    2493        78423 :     bool ErrorsFound = false;
    2494              : 
    2495        78423 :     auto &thisOAController(state.dataMixedAir->OAController(OAControllerNum));
    2496              : 
    2497        78423 :     if (state.dataMixedAir->InitOAControllerOneTimeFlag) {
    2498           29 :         state.dataMixedAir->OAControllerMyOneTimeFlag.dimension(state.dataMixedAir->NumOAControllers, true);
    2499           29 :         state.dataMixedAir->OAControllerMyEnvrnFlag.dimension(state.dataMixedAir->NumOAControllers, true);
    2500           29 :         state.dataMixedAir->OAControllerMySizeFlag.dimension(state.dataMixedAir->NumOAControllers, true);
    2501           29 :         state.dataMixedAir->MechVentCheckFlag.dimension(state.dataMixedAir->NumOAControllers, true);
    2502           29 :         state.dataMixedAir->InitOAControllerSetPointCheckFlag.dimension(state.dataMixedAir->NumOAControllers, true);
    2503           29 :         state.dataMixedAir->InitOAControllerOneTimeFlag = false;
    2504              :     }
    2505        78423 :     if (state.dataMixedAir->OAControllerMyOneTimeFlag(OAControllerNum)) {
    2506              :         // Determine Inlet node index for OAController, not a user input for controller, but is obtained from OutsideAirSys and OAMixer
    2507           29 :         switch (thisOAController.ControllerType) {
    2508           29 :         case MixedAirControllerType::ControllerOutsideAir: {
    2509           29 :             int thisOASys = 0;
    2510           29 :             for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
    2511              :                 // find which OAsys has this controller
    2512           29 :                 int found = Util::FindItemInList(thisOAController.Name,
    2513           29 :                                                  state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName,
    2514           29 :                                                  isize(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName));
    2515           29 :                 if (found != 0) {
    2516           29 :                     thisOASys = OASysNum;
    2517           29 :                     state.dataAirLoop->OutsideAirSys(thisOASys).OAControllerIndex = GetOAController(state, thisOAController.Name);
    2518           29 :                     break; // we found it
    2519              :                 }
    2520              :             }
    2521           29 :             if (thisOASys == 0) {
    2522            0 :                 ShowSevereError(state, format("InitOAController: Did not find OAController=\"{}\".", thisOAController.Name));
    2523            0 :                 ShowContinueError(state, "in list of valid OA Controllers.");
    2524            0 :                 ErrorsFound = true;
    2525              :             }
    2526           29 :             int thisNumForMixer = Util::FindItem(CurrentModuleObjects[static_cast<int>(CMO::OAMixer)],
    2527           29 :                                                  state.dataAirLoop->OutsideAirSys(thisOASys).ComponentType,
    2528           29 :                                                  isize(state.dataAirLoop->OutsideAirSys(thisOASys).ComponentType));
    2529           29 :             if (thisNumForMixer != 0) {
    2530           29 :                 std::string_view const equipName = state.dataAirLoop->OutsideAirSys(thisOASys).ComponentName(thisNumForMixer);
    2531           29 :                 int thisMixerIndex = Util::FindItemInList(equipName, state.dataMixedAir->OAMixer);
    2532           29 :                 if (thisMixerIndex != 0) {
    2533           29 :                     thisOAController.InletNode = state.dataMixedAir->OAMixer(thisMixerIndex).InletNode;
    2534              :                 } else {
    2535            0 :                     ShowSevereError(state, format("InitOAController: Did not find OAMixer=\"{}\".", equipName));
    2536            0 :                     ShowContinueError(state, "in list of valid OA Mixers.");
    2537            0 :                     ErrorsFound = true;
    2538              :                 }
    2539              :             } else {
    2540            0 :                 ShowSevereError(state, "InitOAController: Did not find OutdoorAir:Mixer Component=\"OutdoorAir:Mixer\".");
    2541            0 :                 ShowContinueError(state, "in list of valid OA Components.");
    2542            0 :                 ErrorsFound = true;
    2543              :             }
    2544              : 
    2545           29 :             if (thisOAController.InletNode == 0) { // throw an error
    2546            0 :                 ShowSevereError(
    2547              :                     state,
    2548            0 :                     format("InitOAController: Failed to find proper inlet node for OutdoorAir:Mixer and Controller = {}", thisOAController.Name));
    2549            0 :                 ErrorsFound = true;
    2550              :             }
    2551           29 :         } break;
    2552            0 :         case MixedAirControllerType::ControllerStandAloneERV: {
    2553              :             // set the inlet node to also equal the OA node because this is a special controller for economizing stand alone ERV
    2554              :             // with the assumption that equipment is bypassed....
    2555            0 :             thisOAController.InletNode = thisOAController.OANode;
    2556            0 :         } break;
    2557            0 :         default: {
    2558            0 :             ShowSevereError(state,
    2559            0 :                             format("InitOAController: Failed to find ControllerType: {}",
    2560            0 :                                    MixedAirControllerTypeNames[static_cast<int>(thisOAController.ControllerType)]));
    2561            0 :             ErrorsFound = true;
    2562            0 :         } break;
    2563              :         }
    2564              : 
    2565           29 :         state.dataMixedAir->OAControllerMyOneTimeFlag(OAControllerNum) = false;
    2566              :     }
    2567              : 
    2568       156846 :     if (!state.dataGlobal->SysSizingCalc && state.dataMixedAir->InitOAControllerSetPointCheckFlag(OAControllerNum) &&
    2569       156846 :         state.dataHVACGlobal->DoSetPointTest && !FirstHVACIteration) {
    2570           22 :         int MixedAirNode = thisOAController.MixNode;
    2571           22 :         if (MixedAirNode > 0) {
    2572              :             //      IF (OAController(OAControllerNum)%Econo == 1 .AND. .NOT. AirLoopControlInfo(AirLoopNum)%CyclingFan) THEN
    2573           22 :             if (thisOAController.Econo > EconoOp::NoEconomizer && state.dataAirLoop->AirLoopControlInfo(AirLoopNum).AnyContFan) {
    2574            8 :                 if (state.dataLoopNodes->Node(MixedAirNode).TempSetPoint == SensedNodeFlagValue) {
    2575            0 :                     if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
    2576            0 :                         ShowSevereError(state, format("MixedAir: Missing temperature setpoint for economizer controller {}", thisOAController.Name));
    2577            0 :                         ShowContinueError(state, format("Node Referenced (by Controller)={}", state.dataLoopNodes->NodeID(MixedAirNode)));
    2578            0 :                         ShowContinueError(
    2579              :                             state, "  use a Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at the mixed air node.");
    2580            0 :                         state.dataHVACGlobal->SetPointErrorFlag = true;
    2581              :                     } else {
    2582              :                         // add call to check node in EMS
    2583            0 :                         EMSManager::CheckIfNodeSetPointManagedByEMS(
    2584            0 :                             state, MixedAirNode, HVAC::CtrlVarType::Temp, state.dataHVACGlobal->SetPointErrorFlag);
    2585            0 :                         if (state.dataHVACGlobal->SetPointErrorFlag) {
    2586            0 :                             ShowSevereError(state,
    2587            0 :                                             format("MixedAir: Missing temperature setpoint for economizer controller {}", thisOAController.Name));
    2588            0 :                             ShowContinueError(state, format("Node Referenced (by Controller)={}", state.dataLoopNodes->NodeID(MixedAirNode)));
    2589            0 :                             ShowContinueError(state,
    2590              :                                               "  use a Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at the "
    2591              :                                               "mixed air node.");
    2592            0 :                             ShowContinueError(state, "Or add EMS Actuator to provide temperature setpoint at this node");
    2593              :                         }
    2594              :                     }
    2595              :                 }
    2596              :             }
    2597              :         }
    2598              : 
    2599           22 :         state.dataMixedAir->InitOAControllerSetPointCheckFlag(OAControllerNum) = false;
    2600              :     }
    2601              : 
    2602        78423 :     if (!state.dataGlobal->SysSizingCalc && state.dataMixedAir->OAControllerMySizeFlag(OAControllerNum)) {
    2603           29 :         thisOAController.SizeOAController(state);
    2604           29 :         if (AirLoopNum > 0) {
    2605           28 :             state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OACtrlNum = OAControllerNum;
    2606           28 :             state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OACtrlName = thisOAController.Name;
    2607           28 :             if (thisOAController.Lockout == LockoutType::LockoutWithHeatingPossible) {
    2608            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = true;
    2609            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = false;
    2610            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = false;
    2611           28 :             } else if (thisOAController.Lockout == LockoutType::LockoutWithCompressorPossible) {
    2612            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = false;
    2613            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = true;
    2614            0 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = false;
    2615              :             } else {
    2616           28 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithHeating = false;
    2617           28 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanLockoutEconoWithCompressor = false;
    2618           28 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CanNotLockoutEcono = true;
    2619              :             }
    2620              :         }
    2621           29 :         if ((thisOAController.MaxOA - thisOAController.MinOA) < -HVAC::SmallAirVolFlow) {
    2622            0 :             ShowSevereError(state, format("For Controller:OutdoorAir: {}", thisOAController.Name));
    2623            0 :             ShowContinueError(state,
    2624            0 :                               format("  maximum outdoor air flow rate ({:.4R}) < minimum outdoor air flow rate ({:.4R})",
    2625            0 :                                      thisOAController.MaxOA,
    2626            0 :                                      thisOAController.MinOA));
    2627            0 :             ShowContinueError(state,
    2628              :                               "  To set the minimum outside air flow rate use the \"Design (minimum) outdoor air flow rate\" field in the "
    2629              :                               "Sizing:System object");
    2630            0 :             ErrorsFound = true;
    2631              :         }
    2632              : 
    2633           29 :         if (AirLoopNum > 0) {
    2634           28 :             Real64 DesSupplyVolFlowRate = state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply / state.dataEnvrn->StdRhoAir;
    2635           28 :             if ((thisOAController.MinOA - DesSupplyVolFlowRate) > 0.0001) {
    2636            0 :                 ShowWarningError(state,
    2637            0 :                                  format("InitOAController: Minimum Outdoor Air Flow Rate for Controller:OutdoorAir={} is greater than Design Supply "
    2638              :                                         "Air Flow Rate for AirLoopHVAC={}.",
    2639            0 :                                         thisOAController.Name,
    2640            0 :                                         state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum).Name));
    2641            0 :                 ShowContinueError(state,
    2642            0 :                                   format("...Minimum Outdoor Air Flow Rate={:.6R} will be reset to loop Design Supply Air Flow Rate={:.6R}",
    2643            0 :                                          thisOAController.MinOA,
    2644              :                                          DesSupplyVolFlowRate));
    2645            0 :                 thisOAController.MinOA = DesSupplyVolFlowRate;
    2646           28 :             } else if ((thisOAController.MinOA - DesSupplyVolFlowRate) > 0.0) {
    2647              :                 // If difference is tiny, reset silently
    2648            0 :                 thisOAController.MinOA = DesSupplyVolFlowRate;
    2649              :             }
    2650           28 :             if ((thisOAController.MaxOA - DesSupplyVolFlowRate) > 0.0001) {
    2651            4 :                 ShowWarningError(state,
    2652            4 :                                  format("InitOAController: Maximum Outdoor Air Flow Rate for Controller:OutdoorAir={} is greater than Design Supply "
    2653              :                                         "Air Flow Rate for AirLoopHVAC={}.",
    2654            2 :                                         thisOAController.Name,
    2655            2 :                                         state.dataAirSystemsData->PrimaryAirSystems(AirLoopNum).Name));
    2656            4 :                 ShowContinueError(state,
    2657            4 :                                   format("...Maximum Outdoor Air Flow Rate={:.6R} will be reset to loop Design Supply Air Flow Rate={:.6R}",
    2658            2 :                                          thisOAController.MaxOA,
    2659              :                                          DesSupplyVolFlowRate));
    2660            2 :                 thisOAController.MaxOA = DesSupplyVolFlowRate;
    2661           26 :             } else if ((thisOAController.MaxOA - DesSupplyVolFlowRate) > 0.0) {
    2662              :                 // If difference is tiny, reset silently
    2663            0 :                 thisOAController.MaxOA = DesSupplyVolFlowRate;
    2664              :             }
    2665              : 
    2666              :             // Check if system has a Sizing:System object and a sizing run has been done
    2667           28 :             bool SizingDesRunThisAirSys = false;
    2668           28 :             CheckThisAirSystemForSizing(state, AirLoopNum, SizingDesRunThisAirSys);
    2669              : 
    2670              :             // Get design outdoor air flow rate
    2671           28 :             if (SizingDesRunThisAirSys && thisOAController.VentMechObjectNum > 0) {
    2672            7 :                 state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum).SysDesOA =
    2673            7 :                     state.dataSize->FinalSysSizing(AirLoopNum).DesOutAirVolFlow;
    2674              :             }
    2675              :         }
    2676              : 
    2677           29 :         state.dataMixedAir->OAControllerMySizeFlag(OAControllerNum) = false;
    2678              :     }
    2679              : 
    2680        78423 :     if (state.dataGlobal->BeginEnvrnFlag && state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum)) {
    2681           77 :         Real64 RhoAirStdInit = state.dataEnvrn->StdRhoAir;
    2682           77 :         thisOAController.MinOAMassFlowRate = thisOAController.MinOA * RhoAirStdInit;
    2683           77 :         thisOAController.MaxOAMassFlowRate = thisOAController.MaxOA * RhoAirStdInit;
    2684           77 :         state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum) = false;
    2685           77 :         state.dataLoopNodes->Node(thisOAController.OANode).MassFlowRateMax = thisOAController.MaxOAMassFlowRate;
    2686              : 
    2687              :         // predefined reporting
    2688           77 :         if (thisOAController.Econo > EconoOp::NoEconomizer) {
    2689           22 :             std::string_view const equipName = thisOAController.Name;
    2690              :             // 90.1 descriptor for economizer controls. Changed by Amit for New Feature implementation
    2691           22 :             if (thisOAController.Econo == EconoOp::DifferentialEnthalpy) {
    2692            4 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "DifferentialEnthalpy");
    2693           18 :             } else if (thisOAController.Econo == EconoOp::DifferentialDryBulb) {
    2694           10 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "DifferentialDryBulb");
    2695            8 :             } else if (thisOAController.Econo == EconoOp::FixedEnthalpy) {
    2696            0 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "FixedEnthalpy");
    2697            8 :             } else if (thisOAController.Econo == EconoOp::FixedDryBulb) {
    2698            8 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "FixedDryBulb");
    2699              :             } else {
    2700            0 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoKind, equipName, "Other");
    2701              :             }
    2702              : 
    2703           22 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoMinOA, equipName, thisOAController.MinOA);
    2704           22 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoMaxOA, equipName, thisOAController.MaxOA);
    2705              :             // EnergyPlus input echos for economizer controls. Changed by Amit for new feature implementation
    2706           22 :             if (thisOAController.Econo == EconoOp::DifferentialDryBulb) {
    2707           10 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "Yes");
    2708              :             } else {
    2709           12 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "No");
    2710              :             }
    2711           22 :             if (thisOAController.Econo == EconoOp::DifferentialEnthalpy) {
    2712            4 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "Yes");
    2713              :             } else {
    2714           18 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "No");
    2715              :             }
    2716           22 :             if (thisOAController.Econo == EconoOp::FixedDryBulb) {
    2717            8 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, thisOAController.TempLim);
    2718              :             } else {
    2719           14 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "-");
    2720              :             }
    2721           22 :             if (thisOAController.Econo == EconoOp::FixedEnthalpy) {
    2722            0 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, thisOAController.EnthLim);
    2723              :             } else {
    2724           22 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEcoRetTemp, equipName, "-");
    2725              :             }
    2726              :         }
    2727              :     }
    2728              : 
    2729        78423 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    2730        77640 :         state.dataMixedAir->OAControllerMyEnvrnFlag(OAControllerNum) = true;
    2731              :     }
    2732              : 
    2733        78423 :     if (state.dataMixedAir->MechVentCheckFlag(OAControllerNum)) {
    2734              :         // Make these checks only once at the beginning of the simulation
    2735              : 
    2736              :         // Make sure all air loop zones and air loop zones with people objects are covered by mechanical ventilation
    2737              :         // Issue a warning only if the zone is not accounted for in the associated mechanical ventilation object
    2738           29 :         if (thisOAController.VentMechObjectNum > 0) {
    2739            9 :             auto &vent_mech(state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum));
    2740              : 
    2741              :             // Make sure all zones with mechanical ventilation are on the correct air loop
    2742            9 :             int TempMechVentArrayCounter = 0;
    2743           26 :             for (int NumMechVentZone = 1; NumMechVentZone <= vent_mech.NumofVentMechZones; ++NumMechVentZone) {
    2744           17 :                 auto &thisMechVentZone = vent_mech.VentMechZone(NumMechVentZone);
    2745           17 :                 int ZoneNum = thisMechVentZone.zoneNum;
    2746           17 :                 auto const &zone(state.dataHeatBal->Zone(ZoneNum));
    2747           17 :                 bool FoundZone = false;
    2748              : 
    2749           26 :                 for (int AirLoopZoneInfoZoneNum = 1; AirLoopZoneInfoZoneNum <= state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).NumZones;
    2750              :                      ++AirLoopZoneInfoZoneNum) {
    2751           26 :                     int NumZone = state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).ActualZoneNumber(AirLoopZoneInfoZoneNum);
    2752           26 :                     if (ZoneNum == NumZone) {
    2753           17 :                         FoundZone = true;
    2754           17 :                         ++TempMechVentArrayCounter;
    2755           17 :                         if (TempMechVentArrayCounter < NumMechVentZone) { // Copy to lower index
    2756            0 :                             auto &tempMechVentZone = vent_mech.VentMechZone(TempMechVentArrayCounter);
    2757            0 :                             tempMechVentZone.zoneNum = thisMechVentZone.zoneNum;
    2758            0 :                             tempMechVentZone.ZoneOAAreaRate = thisMechVentZone.ZoneOAAreaRate;
    2759            0 :                             tempMechVentZone.ZoneOAPeopleRate = thisMechVentZone.ZoneOAPeopleRate;
    2760            0 :                             tempMechVentZone.ZoneOAFlowRate = thisMechVentZone.ZoneOAFlowRate;
    2761            0 :                             tempMechVentZone.ZoneOAACHRate = thisMechVentZone.ZoneOAACHRate;
    2762            0 :                             tempMechVentZone.ZoneOAFlowMethod = thisMechVentZone.ZoneOAFlowMethod;
    2763            0 :                             tempMechVentZone.zoneOASched = thisMechVentZone.zoneOASched;
    2764            0 :                             tempMechVentZone.ZoneDesignSpecOAObjIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex;
    2765            0 :                             tempMechVentZone.ZoneDesignSpecOAObjName = thisMechVentZone.ZoneDesignSpecOAObjName;
    2766              : 
    2767              :                             // new DCV
    2768            0 :                             tempMechVentZone.ZoneADEffCooling = thisMechVentZone.ZoneADEffCooling;
    2769            0 :                             tempMechVentZone.ZoneADEffHeating = thisMechVentZone.ZoneADEffHeating;
    2770            0 :                             tempMechVentZone.zoneADEffSched = thisMechVentZone.zoneADEffSched;
    2771              :                         }
    2772              : 
    2773              :                         // Sum outside air per unit floor area for each mechanical ventilation object only once per simulation
    2774           17 :                         vent_mech.TotAreaOAFlow += zone.FloorArea * zone.Multiplier * zone.ListMultiplier * thisMechVentZone.ZoneOAAreaRate;
    2775           17 :                         vent_mech.TotZoneOAFlow += zone.Multiplier * zone.ListMultiplier * thisMechVentZone.ZoneOAFlowRate;
    2776           17 :                         vent_mech.TotZoneOAACH += zone.Multiplier * zone.ListMultiplier * (thisMechVentZone.ZoneOAACHRate * zone.Volume / 3600.0);
    2777           17 :                         break;
    2778              :                     }
    2779              :                 }
    2780           17 :                 if (!FoundZone) {
    2781            0 :                     ShowWarningError(state,
    2782            0 :                                      format("Zone name = {} in {} object name = {} is not on the same air loop as Controller:OutdoorAir = {}",
    2783            0 :                                             zone.Name,
    2784            0 :                                             CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
    2785            0 :                                             thisOAController.VentilationMechanicalName,
    2786            0 :                                             thisOAController.Name));
    2787            0 :                     ShowContinueError(state, "This zone will not be used and the simulation will continue...");
    2788              :                 }
    2789              :             }
    2790              : 
    2791              :             // Shrink final arrays to conserve environment space
    2792            9 :             if (TempMechVentArrayCounter < vent_mech.NumofVentMechZones) {
    2793            0 :                 vent_mech.VentMechZone.resize(TempMechVentArrayCounter);
    2794            0 :                 vent_mech.NumofVentMechZones = TempMechVentArrayCounter;
    2795              :             }
    2796              : 
    2797              :             // predefined report
    2798           26 :             for (int jZone = 1; jZone <= vent_mech.NumofVentMechZones; ++jZone) {
    2799           17 :                 auto &thisMechVentZone = vent_mech.VentMechZone(jZone);
    2800           17 :                 std::string_view const zoneName = state.dataHeatBal->Zone(thisMechVentZone.zoneNum).Name;
    2801           17 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVventMechName, zoneName, vent_mech.Name);
    2802           17 :                 OutputReportPredefined::PreDefTableEntry(
    2803           17 :                     state, state.dataOutRptPredefined->pdchDCVperPerson, zoneName, thisMechVentZone.ZoneOAPeopleRate, 6);
    2804           17 :                 OutputReportPredefined::PreDefTableEntry(
    2805           17 :                     state, state.dataOutRptPredefined->pdchDCVperArea, zoneName, thisMechVentZone.ZoneOAAreaRate, 6);
    2806           17 :                 OutputReportPredefined::PreDefTableEntry(
    2807           17 :                     state, state.dataOutRptPredefined->pdchDCVperZone, zoneName, thisMechVentZone.ZoneOAFlowRate, 6);
    2808           17 :                 OutputReportPredefined::PreDefTableEntry(
    2809           17 :                     state, state.dataOutRptPredefined->pdchDCVperACH, zoneName, thisMechVentZone.ZoneOAACHRate, 6);
    2810           17 :                 OutputReportPredefined::PreDefTableEntry(state,
    2811           17 :                                                          state.dataOutRptPredefined->pdchDCVMethod,
    2812              :                                                          zoneName,
    2813           17 :                                                          OAFlowCalcMethodNames[static_cast<int>(thisMechVentZone.ZoneOAFlowMethod)]);
    2814           17 :                 OutputReportPredefined::PreDefTableEntry(
    2815           17 :                     state, state.dataOutRptPredefined->pdchDCVType, zoneName, SysOAMethodNames[static_cast<int>(vent_mech.SystemOAMethod)]);
    2816           17 :                 if (thisMechVentZone.zoneOASched != nullptr) {
    2817           17 :                     OutputReportPredefined::PreDefTableEntry(
    2818           17 :                         state, state.dataOutRptPredefined->pdchDCVOASchName, zoneName, thisMechVentZone.zoneOASched->Name);
    2819              :                 } else {
    2820            0 :                     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVOASchName, zoneName, "");
    2821              :                 }
    2822              : 
    2823              :                 // added for new DCV inputs
    2824           17 :                 if (thisMechVentZone.zoneADEffSched != nullptr) {
    2825            4 :                     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffCooling, zoneName, "");
    2826            4 :                     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffHeating, zoneName, "");
    2827            4 :                     OutputReportPredefined::PreDefTableEntry(
    2828            4 :                         state, state.dataOutRptPredefined->pdchDCVZoneADEffSchName, zoneName, thisMechVentZone.zoneADEffSched->Name);
    2829              :                 } else {
    2830           13 :                     OutputReportPredefined::PreDefTableEntry(
    2831           13 :                         state, state.dataOutRptPredefined->pdchDCVZoneADEffCooling, zoneName, thisMechVentZone.ZoneADEffCooling, 2);
    2832           13 :                     OutputReportPredefined::PreDefTableEntry(
    2833           13 :                         state, state.dataOutRptPredefined->pdchDCVZoneADEffHeating, zoneName, thisMechVentZone.ZoneADEffHeating, 2);
    2834           13 :                     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDCVZoneADEffSchName, zoneName, "");
    2835              :                 }
    2836              :             }
    2837              : 
    2838              :             // Fill People index lists if needed
    2839            9 :             if (vent_mech.SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    2840            6 :                 for (int peopleNum = 1; peopleNum <= state.dataHeatBal->TotPeople; ++peopleNum) {
    2841            7 :                     for (auto &thisMechVentZone : vent_mech.VentMechZone) {
    2842            7 :                         if (state.dataHeatBal->People(peopleNum).ZonePtr == thisMechVentZone.zoneNum) {
    2843            4 :                             thisMechVentZone.peopleIndexes.push_back(peopleNum);
    2844            4 :                             break;
    2845              :                         }
    2846            4 :                     }
    2847              :                 }
    2848              :             }
    2849              : 
    2850              :             // Check to see if any zones on an air loop are not accounted for by a mechanical ventilation object
    2851           26 :             for (int AirLoopZoneInfoZoneNum = 1; AirLoopZoneInfoZoneNum <= state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).NumZones;
    2852              :                  ++AirLoopZoneInfoZoneNum) {
    2853           17 :                 int NumZone = state.dataAirLoop->AirLoopZoneInfo(AirLoopNum).ActualZoneNumber(AirLoopZoneInfoZoneNum);
    2854           17 :                 bool FoundAreaZone = false;
    2855           17 :                 bool FoundPeopleZone = false;
    2856           26 :                 for (int NumMechVentZone = 1; NumMechVentZone <= vent_mech.NumofVentMechZones; ++NumMechVentZone) {
    2857           26 :                     auto const &thisMechVentZone = vent_mech.VentMechZone(NumMechVentZone);
    2858           26 :                     int ZoneNum = thisMechVentZone.zoneNum;
    2859           26 :                     if (ZoneNum == NumZone) {
    2860           17 :                         FoundAreaZone = true;
    2861           17 :                         if (thisMechVentZone.ZoneOAPeopleRate > 0.0) {
    2862           11 :                             FoundPeopleZone = true;
    2863              :                         }
    2864           17 :                         break;
    2865              :                     }
    2866              :                 }
    2867           17 :                 if (!FoundAreaZone) {
    2868            0 :                     ShowWarningError(state,
    2869            0 :                                      format("Zone name = {} is not accounted for by {} object name = {}",
    2870            0 :                                             state.dataHeatBal->Zone(NumZone).Name,
    2871            0 :                                             CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
    2872            0 :                                             thisOAController.VentilationMechanicalName));
    2873            0 :                     ShowContinueError(state, "Ventilation per unit floor area has not been specified for this zone, which is connected to");
    2874            0 :                     ShowContinueError(
    2875            0 :                         state, format("the air loop served by Controller:OutdoorAir = {}. Simulation will continue...", thisOAController.Name));
    2876              :                 }
    2877           17 :                 if (!FoundPeopleZone) {
    2878              :                     // Loop through people objects to see if this zone has a people object and only then show a warning
    2879           18 :                     for (int PeopleNum = 1; PeopleNum <= state.dataHeatBal->TotPeople; ++PeopleNum) {
    2880           12 :                         if (state.dataHeatBal->People(PeopleNum).ZonePtr == NumZone) {
    2881            6 :                             if (!FoundAreaZone) {
    2882            0 :                                 ShowWarningError(state,
    2883            0 :                                                  format("PEOPLE object for zone = {} is not accounted for by {} object name = {}",
    2884            0 :                                                         state.dataHeatBal->Zone(NumZone).Name,
    2885            0 :                                                         CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
    2886            0 :                                                         thisOAController.VentilationMechanicalName));
    2887            0 :                                 ShowContinueError(
    2888              :                                     state,
    2889            0 :                                     format(
    2890              :                                         "A \"PEOPLE\" object has been specified in the idf for this zone, but it is not included in this {} Object.",
    2891            0 :                                         CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]));
    2892            0 :                                 ShowContinueError(state,
    2893            0 :                                                   format("Check {} object. Simulation will continue.",
    2894            0 :                                                          CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]));
    2895              :                             }
    2896              :                         }
    2897              :                     }
    2898              :                 } else { // People > 0, check to make sure there is a people statement in the zone
    2899           11 :                     FoundAreaZone = false;
    2900           20 :                     for (int PeopleNum = 1; PeopleNum <= state.dataHeatBal->TotPeople; ++PeopleNum) {
    2901           20 :                         if (state.dataHeatBal->People(PeopleNum).ZonePtr != NumZone) {
    2902            9 :                             continue;
    2903              :                         }
    2904           11 :                         FoundAreaZone = true;
    2905           11 :                         break;
    2906              :                     }
    2907           11 :                     if (!FoundAreaZone) {
    2908            0 :                         ShowWarningError(state,
    2909            0 :                                          format("{} = \"{}\", Zone=\"{}\".",
    2910            0 :                                                 CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)],
    2911            0 :                                                 thisOAController.VentilationMechanicalName,
    2912            0 :                                                 state.dataHeatBal->Zone(NumZone).Name));
    2913            0 :                         ShowContinueError(state,
    2914              :                                           "No \"PEOPLE\" object has been specified in the idf for this zone, but the ventilation rate is > 0 in "
    2915              :                                           "this Controller:MechanicalVentilation Object.");
    2916            0 :                         ShowContinueError(state, "Check ventilation rate in Controller:MechanicalVentilation object.  Simulation will continue.");
    2917              :                     }
    2918              :                 }
    2919              :             }
    2920              :         }
    2921              : 
    2922           29 :         state.dataMixedAir->MechVentCheckFlag(OAControllerNum) = false;
    2923              :     }
    2924              :     //****
    2925              : 
    2926              :     // Perform a one time initialization of AirloopHVAC OA System report variables
    2927              :     // If AirloopHVAC objects are used, NumPrimaryAirSys > 0 and the initialization proceeds and then sets
    2928              :     // SetUpAirLoopHVACVariables to .FALSE. so this is never done again and only the first IF is checked
    2929              :     // each time through Init. If for some reason the primary air system have not yet been read in, this
    2930              :     // code waits for the air loop data to be available before performing the report variable initialization.
    2931              :     // If AirloopHVAC objects are not used, NumPrimaryAirSys is always equal to 0 and only these
    2932              :     // two IF statements are checked each time through Init (e.g., if StandAloneERV controllers are used
    2933              :     // without AirloopHVAC objects).
    2934        78423 :     if (state.dataMixedAir->InitOAControllerSetUpAirLoopHVACVariables) {
    2935           29 :         if (AirLoopNum > 0) {
    2936              :             // Added code to report (TH, 10/20/2008):
    2937              :             //   air economizer status (1 = on, 0 = off or does not exist), and actual and minimum outside air fraction (0 to 1)
    2938           28 :             std::string airloopName; // Temporary equipment name
    2939           56 :             for (int OAControllerLoop = 1; OAControllerLoop <= state.dataMixedAir->NumOAControllers; ++OAControllerLoop) {
    2940           28 :                 auto &loopOAController(state.dataMixedAir->OAController(OAControllerLoop));
    2941              : 
    2942              :                 // Find the outside air system that has the OA controller
    2943           28 :                 if (loopOAController.ControllerType == MixedAirControllerType::ControllerStandAloneERV) {
    2944            0 :                     continue; // ERV controller not on airloop
    2945              :                 }
    2946           28 :                 bool OASysFound = false;
    2947           28 :                 int thisOASys = 0;
    2948           28 :                 for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; ++OASysNum) {
    2949           28 :                     for (int OAControllerLoop2 = 1; OAControllerLoop2 <= state.dataAirLoop->OutsideAirSys(OASysNum).NumControllers;
    2950              :                          ++OAControllerLoop2) {
    2951           28 :                         if (Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerName(OAControllerLoop2), loopOAController.Name)) {
    2952           28 :                             thisOASys = OASysNum;
    2953           28 :                             OASysFound = true;
    2954           28 :                             break;
    2955              :                         }
    2956              :                     }
    2957           28 :                     if (OASysFound) {
    2958           28 :                         break;
    2959              :                     }
    2960              :                 }
    2961              : 
    2962           28 :                 int airLoopNum = 0;
    2963           28 :                 bool AirLoopFound = false;
    2964           28 :                 if (thisOASys <= 0) {
    2965              :                     // Check outside air system name
    2966            0 :                     ShowWarningError(state, format("Cannot find the AirLoopHVAC:OutdoorAirSystem for the OA Controller: {}", loopOAController.Name));
    2967              :                 } else {
    2968              :                     // Find the primary air loop that has the outside air system
    2969           28 :                     for (int thisAirLoop = 1; thisAirLoop <= state.dataHVACGlobal->NumPrimaryAirSys; ++thisAirLoop) {
    2970           26 :                         for (int BranchNum = 1; BranchNum <= state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).NumBranches; ++BranchNum) {
    2971           27 :                             for (int CompNum = 1;
    2972           27 :                                  CompNum <= state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).TotalComponents;
    2973              :                                  ++CompNum) {
    2974           27 :                                 if (!Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).Comp(CompNum).Name,
    2975           80 :                                                       state.dataAirLoop->OutsideAirSys(thisOASys).Name) ||
    2976           53 :                                     !Util::SameString(state.dataAirSystemsData->PrimaryAirSystems(thisAirLoop).Branch(BranchNum).Comp(CompNum).TypeOf,
    2977              :                                                       "AirLoopHVAC:OutdoorAirSystem")) {
    2978            1 :                                     continue;
    2979              :                                 }
    2980           26 :                                 AirLoopFound = true;
    2981           26 :                                 airLoopNum = thisAirLoop;
    2982           26 :                                 break;
    2983              :                             }
    2984           26 :                             if (AirLoopFound) {
    2985           26 :                                 break;
    2986              :                             }
    2987              :                         }
    2988           26 :                         if (AirLoopFound) {
    2989           26 :                             break;
    2990              :                         }
    2991              :                     }
    2992              :                 }
    2993              :                 // Check primary air loop name
    2994           28 :                 if (AirLoopFound && airLoopNum > 0) {
    2995           26 :                     airloopName = state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name; // OutsideAirSys(OASysIndex)%Name
    2996              :                 } else {
    2997            2 :                     ShowWarningError(state, format("Cannot find the primary air loop for the OA Controller: {}", loopOAController.Name));
    2998            2 :                     airloopName = "AirLoop not found";
    2999              :                 }
    3000              : 
    3001              :                 //    Note use of OAControllerLoop here to keep DO Loop index separate from InitOAController local variable
    3002              :                 // CurrentModuleObject='AirLoopHVAC'
    3003           28 :                 SetupOutputVariable(state,
    3004              :                                     "Air System Outdoor Air Economizer Status",
    3005              :                                     Constant::Units::None,
    3006           28 :                                     loopOAController.EconomizerStatus,
    3007              :                                     OutputProcessor::TimeStepType::System,
    3008              :                                     OutputProcessor::StoreType::Average,
    3009              :                                     airloopName);
    3010              : 
    3011           28 :                 SetupOutputVariable(state,
    3012              :                                     "Air System Outdoor Air Heat Recovery Bypass Status",
    3013              :                                     Constant::Units::None,
    3014           28 :                                     loopOAController.HeatRecoveryBypassStatus,
    3015              :                                     OutputProcessor::TimeStepType::System,
    3016              :                                     OutputProcessor::StoreType::Average,
    3017              :                                     airloopName);
    3018              : 
    3019           28 :                 SetupOutputVariable(state,
    3020              :                                     "Air System Outdoor Air Heat Recovery Bypass Heating Coil Activity Status",
    3021              :                                     Constant::Units::None,
    3022           28 :                                     loopOAController.HRHeatingCoilActive,
    3023              :                                     OutputProcessor::TimeStepType::System,
    3024              :                                     OutputProcessor::StoreType::Average,
    3025              :                                     airloopName);
    3026           56 :                 SetupOutputVariable(state,
    3027              :                                     "Air System Outdoor Air Heat Recovery Bypass Minimum Outdoor Air Mixed Air Temperature",
    3028              :                                     Constant::Units::C,
    3029           28 :                                     loopOAController.MixedAirTempAtMinOAFlow,
    3030              :                                     OutputProcessor::TimeStepType::System,
    3031              :                                     OutputProcessor::StoreType::Average,
    3032              :                                     airloopName);
    3033              : 
    3034           28 :                 SetupOutputVariable(state,
    3035              :                                     "Air System Outdoor Air High Humidity Control Status",
    3036              :                                     Constant::Units::None,
    3037           28 :                                     loopOAController.HighHumCtrlStatus,
    3038              :                                     OutputProcessor::TimeStepType::System,
    3039              :                                     OutputProcessor::StoreType::Average,
    3040              :                                     airloopName);
    3041              : 
    3042           28 :                 SetupOutputVariable(state,
    3043              :                                     "Air System Outdoor Air Limiting Factor",
    3044              :                                     Constant::Units::None,
    3045           28 :                                     loopOAController.OALimitingFactorReport,
    3046              :                                     OutputProcessor::TimeStepType::System,
    3047              :                                     OutputProcessor::StoreType::Average,
    3048              :                                     airloopName);
    3049              : 
    3050           56 :                 SetupOutputVariable(state,
    3051              :                                     "Air System Outdoor Air Flow Fraction",
    3052              :                                     Constant::Units::None,
    3053           28 :                                     loopOAController.OAFractionRpt,
    3054              :                                     OutputProcessor::TimeStepType::System,
    3055              :                                     OutputProcessor::StoreType::Average,
    3056              :                                     airloopName);
    3057              : 
    3058           56 :                 SetupOutputVariable(state,
    3059              :                                     "Air System Outdoor Air Minimum Flow Fraction",
    3060              :                                     Constant::Units::None,
    3061           28 :                                     loopOAController.MinOAFracLimit,
    3062              :                                     OutputProcessor::TimeStepType::System,
    3063              :                                     OutputProcessor::StoreType::Average,
    3064              :                                     airloopName);
    3065              : 
    3066           56 :                 SetupOutputVariable(state,
    3067              :                                     "Air System Outdoor Air Mass Flow Rate",
    3068              :                                     Constant::Units::kg_s,
    3069           28 :                                     loopOAController.OAMassFlow,
    3070              :                                     OutputProcessor::TimeStepType::System,
    3071              :                                     OutputProcessor::StoreType::Average,
    3072              :                                     airloopName);
    3073              : 
    3074           56 :                 SetupOutputVariable(state,
    3075              :                                     "Air System Mixed Air Mass Flow Rate",
    3076              :                                     Constant::Units::kg_s,
    3077           28 :                                     loopOAController.MixMassFlow,
    3078              :                                     OutputProcessor::TimeStepType::System,
    3079              :                                     OutputProcessor::StoreType::Average,
    3080              :                                     airloopName);
    3081              : 
    3082           56 :                 SetupOutputVariable(state,
    3083              :                                     "Air System Relief Air Heat Transfer Rate",
    3084              :                                     Constant::Units::W,
    3085           28 :                                     loopOAController.RelTotalLossRate,
    3086              :                                     OutputProcessor::TimeStepType::System,
    3087              :                                     OutputProcessor::StoreType::Average,
    3088              :                                     airloopName);
    3089              : 
    3090           56 :                 SetupOutputVariable(state,
    3091              :                                     "Air System Relief Air Sensible Heat Transfer Rate",
    3092              :                                     Constant::Units::W,
    3093           28 :                                     loopOAController.RelSensiLossRate,
    3094              :                                     OutputProcessor::TimeStepType::System,
    3095              :                                     OutputProcessor::StoreType::Average,
    3096              :                                     airloopName);
    3097              : 
    3098           56 :                 SetupOutputVariable(state,
    3099              :                                     "Air System Relief Air Latent Heat Transfer Rate",
    3100              :                                     Constant::Units::W,
    3101           28 :                                     loopOAController.RelLatentLossRate,
    3102              :                                     OutputProcessor::TimeStepType::System,
    3103              :                                     OutputProcessor::StoreType::Average,
    3104              :                                     airloopName);
    3105              : 
    3106           28 :                 if (loopOAController.MixedAirSPMNum > 0) {
    3107            0 :                     SetupOutputVariable(state,
    3108              :                                         "Air System Outdoor Air Maximum Flow Fraction",
    3109              :                                         Constant::Units::None,
    3110            0 :                                         loopOAController.MaxOAFracBySetPoint,
    3111              :                                         OutputProcessor::TimeStepType::System,
    3112              :                                         OutputProcessor::StoreType::Average,
    3113              :                                         airloopName);
    3114              :                 }
    3115              : 
    3116           28 :                 if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
    3117            0 :                     SetupEMSInternalVariable(
    3118            0 :                         state, "Outdoor Air Controller Maximum Mass Flow Rate", loopOAController.Name, "[kg/s]", loopOAController.MaxOAMassFlowRate);
    3119            0 :                     SetupEMSInternalVariable(
    3120            0 :                         state, "Outdoor Air Controller Minimum Mass Flow Rate", loopOAController.Name, "[kg/s]", loopOAController.MinOAMassFlowRate);
    3121            0 :                     SetupEMSActuator(state,
    3122              :                                      "Outdoor Air Controller",
    3123              :                                      loopOAController.Name,
    3124              :                                      "Air Mass Flow Rate",
    3125              :                                      "[kg/s]",
    3126            0 :                                      loopOAController.EMSOverrideOARate,
    3127            0 :                                      loopOAController.EMSOARateValue);
    3128              :                 }
    3129              : 
    3130           28 :                 if (loopOAController.VentMechObjectNum > 0 && airLoopNum > 0) {
    3131           18 :                     SetupOutputVariable(state,
    3132              :                                         "Air System Outdoor Air Mechanical Ventilation Requested Mass Flow Rate",
    3133              :                                         Constant::Units::kg_s,
    3134            9 :                                         loopOAController.MechVentOAMassFlowRequest,
    3135              :                                         OutputProcessor::TimeStepType::System,
    3136              :                                         OutputProcessor::StoreType::Average,
    3137              :                                         airloopName);
    3138            9 :                     if (!state.dataMixedAir->VentilationMechanical(loopOAController.VentMechObjectNum).DCVFlag) {
    3139            1 :                         state.dataAirLoop->AirLoopControlInfo(airLoopNum).AirLoopDCVFlag = false;
    3140              :                     }
    3141              :                 }
    3142              :             }
    3143              : 
    3144           28 :             state.dataMixedAir->InitOAControllerSetUpAirLoopHVACVariables = false;
    3145           28 :         }
    3146              :     }
    3147              : 
    3148              :     // Each time step
    3149        78423 :     if (FirstHVACIteration) {
    3150              :         // Mixed air setpoint. Set by a setpoint manager.
    3151        38979 :         if (thisOAController.ControllerType == MixedAirControllerType::ControllerOutsideAir) {
    3152        38979 :             if (state.dataLoopNodes->Node(thisOAController.MixNode).TempSetPoint > SensedNodeFlagValue) {
    3153        28682 :                 thisOAController.MixSetTemp = state.dataLoopNodes->Node(thisOAController.MixNode).TempSetPoint;
    3154              :             } else {
    3155        10297 :                 thisOAController.MixSetTemp = thisOAController.TempLowLim;
    3156              :             }
    3157              : 
    3158        38979 :             Real64 TotalPeopleOAFlow = 0.0;
    3159        38979 :             if (thisOAController.VentMechObjectNum != 0) {
    3160         9311 :                 auto &vent_mech(state.dataMixedAir->VentilationMechanical(thisOAController.VentMechObjectNum));
    3161        26884 :                 for (int ZoneIndex = 1; ZoneIndex <= vent_mech.NumofVentMechZones; ++ZoneIndex) {
    3162        17573 :                     auto &thisVentMechZone = vent_mech.VentMechZone(ZoneIndex);
    3163        17573 :                     int ZoneNum = thisVentMechZone.zoneNum;
    3164              : 
    3165              :                     // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
    3166        17573 :                     OAFlowCalcMethod OAFlowMethod = thisVentMechZone.ZoneOAFlowMethod;
    3167        17573 :                     if (OAFlowMethod == OAFlowCalcMethod::PerPerson || OAFlowMethod == OAFlowCalcMethod::Sum ||
    3168              :                         OAFlowMethod == OAFlowCalcMethod::Max) {
    3169        17569 :                         TotalPeopleOAFlow += state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * state.dataHeatBal->Zone(ZoneNum).Multiplier *
    3170        35138 :                                              state.dataHeatBal->Zone(ZoneNum).ListMultiplier * thisVentMechZone.ZoneOAPeopleRate *
    3171        17569 :                                              thisVentMechZone.zoneOASched->getCurrentVal();
    3172              :                     }
    3173              :                 }
    3174         9311 :                 vent_mech.TotPeopleOAFlow = TotalPeopleOAFlow;
    3175              :             }
    3176              :         } else {
    3177              :             // Stand Alone ERV does not require a temperature setpoint schedule, make setpoint equal to lower economizer limit
    3178            0 :             thisOAController.MixSetTemp = thisOAController.TempLowLim;
    3179              :         }
    3180              :     }
    3181              : 
    3182              :     // Each iteration
    3183              : 
    3184        78423 :     if (thisOAController.ControllerType == MixedAirControllerType::ControllerOutsideAir) {
    3185              :         // zone exhaust mass flow is saved in AirLoopFlow%ZoneExhaust
    3186              :         // the zone exhaust mass flow that is said to be balanced by simple air flows is saved in AirLoopFlow%ZoneExhaustBalanced
    3187        78423 :         if (AirLoopNum > 0) {
    3188        78422 :             thisOAController.ExhMassFlow =
    3189        78422 :                 max(0.0, state.dataAirLoop->AirLoopFlow(AirLoopNum).SupFlow - state.dataAirLoop->AirLoopFlow(AirLoopNum).SysRetFlow);
    3190        78422 :             state.dataAirLoop->AirLoopControlInfo(AirLoopNum).ZoneExhMassFlow = thisOAController.ExhMassFlow;
    3191        78422 :             if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).LoopFlowRateSet && !FirstHVACIteration) {
    3192              :                 // if flow rate has been specified by a manager, set it to the specified value
    3193            0 :                 thisOAController.MixMassFlow =
    3194            0 :                     state.dataAirLoop->AirLoopFlow(AirLoopNum).ReqSupplyFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply;
    3195              :                 // state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate = thisOAController.MixMassFlow - thisOAController.ExhMassFlow;
    3196              :             } else {
    3197        78422 :                 thisOAController.MixMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow;
    3198              : 
    3199              :                 // The following was commented out after discussion on PR 7382, it can be reopened for discussion anytime
    3200              :                 // found this equation results in flow that exceeds the design flow rate when there is exhaust flow rate is greater than
    3201              :                 // the design supply air flow rate. Capped the mixed air flow rate at design supply air flow rate, issue #77379
    3202              :                 // thisOAController.MixMassFlow = Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow;
    3203              :                 // thisOAController.MixMassFlow =
    3204              :                 //     min(Node(thisOAController.RetNode).MassFlowRate + thisOAController.ExhMassFlow, AirLoopFlow(AirLoopNum).DesSupply);
    3205              :             }
    3206              :         } else {
    3207            1 :             thisOAController.ExhMassFlow = 0.0;
    3208            1 :             thisOAController.MixMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate;
    3209              :         }
    3210        78423 :         if (state.dataLoopNodes->Node(thisOAController.MixNode).MassFlowRateMaxAvail <= 0.0) {
    3211         4445 :             thisOAController.MixMassFlow = 0.0;
    3212              :         }
    3213              :     } else {
    3214              :         // Mixed and exhaust flow rates are passed through to model CONTROLLER:STAND ALONE ERV in SimOAController
    3215            0 :         thisOAController.OAMassFlow = thisOAController.MaxOAMassFlowRate;
    3216            0 :         thisOAController.MixMassFlow = thisOAController.MaxOAMassFlowRate;
    3217            0 :         thisOAController.ExhMassFlow = state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate;
    3218              :     }
    3219        78423 :     thisOAController.ExhMassFlow = max(thisOAController.ExhMassFlow, 0.0);
    3220              : 
    3221              :     // Outside air values
    3222        78423 :     thisOAController.OATemp = state.dataLoopNodes->Node(thisOAController.OANode).Temp;
    3223        78423 :     thisOAController.OAEnth = state.dataLoopNodes->Node(thisOAController.OANode).Enthalpy;
    3224        78423 :     thisOAController.OAPress = state.dataLoopNodes->Node(thisOAController.OANode).Press;
    3225        78423 :     thisOAController.OAHumRat = state.dataLoopNodes->Node(thisOAController.OANode).HumRat;
    3226              : 
    3227              :     // Inlet air values (on OA input side)
    3228        78423 :     thisOAController.InletTemp = state.dataLoopNodes->Node(thisOAController.InletNode).Temp;
    3229        78423 :     thisOAController.InletEnth = state.dataLoopNodes->Node(thisOAController.InletNode).Enthalpy;
    3230        78423 :     thisOAController.InletPress = state.dataLoopNodes->Node(thisOAController.InletNode).Press;
    3231        78423 :     thisOAController.InletHumRat = state.dataLoopNodes->Node(thisOAController.InletNode).HumRat;
    3232              : 
    3233              :     // Return air values
    3234        78423 :     thisOAController.RetTemp = state.dataLoopNodes->Node(thisOAController.RetNode).Temp;
    3235        78423 :     thisOAController.RetEnth = state.dataLoopNodes->Node(thisOAController.RetNode).Enthalpy;
    3236              : 
    3237              :     // Check sensors faults for the air economizer
    3238        78423 :     EconoOp iEco = thisOAController.Econo;
    3239        78423 :     if (state.dataFaultsMgr->AnyFaultsInModel && (iEco != EconoOp::NoEconomizer)) {
    3240            0 :         for (int i = 1; i <= thisOAController.NumFaultyEconomizer; ++i) {
    3241            0 :             int j = thisOAController.EconmizerFaultNum(i);
    3242            0 :             Real64 rSchVal = 0.0;
    3243            0 :             if (state.dataFaultsMgr->FaultsEconomizer(j).availSched->getCurrentVal() > 0.0) {
    3244            0 :                 rSchVal = 1.0;
    3245            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).severitySched != nullptr) {
    3246            0 :                     rSchVal = state.dataFaultsMgr->FaultsEconomizer(j).severitySched->getCurrentVal();
    3247              :                 }
    3248              :             } else {
    3249            0 :                 continue; // no fault
    3250              :             }
    3251              : 
    3252            0 :             Real64 rOffset = rSchVal * state.dataFaultsMgr->FaultsEconomizer(j).Offset;
    3253              : 
    3254            0 :             if (std::abs(rOffset) < 0.000000001) {
    3255            0 :                 continue;
    3256              :             }
    3257              : 
    3258              :             // ECONOMIZER - outdoor air dry-bulb temperature sensor offset
    3259            0 :             switch (iEco) {
    3260            0 :             case EconoOp::FixedDryBulb:
    3261              :             case EconoOp::DifferentialDryBulb:
    3262              :             case EconoOp::FixedDewPointAndDryBulb:
    3263              :             case EconoOp::ElectronicEnthalpy:
    3264              :             case EconoOp::DifferentialDryBulbAndEnthalpy: {
    3265            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::TemperatureSensorOffset_OutdoorAir) {
    3266              :                     // FaultModel:TemperatureSensorOffset:OutdoorAir
    3267            0 :                     thisOAController.OATemp += rOffset;
    3268            0 :                     thisOAController.InletTemp += rOffset;
    3269              :                 }
    3270            0 :             } break;
    3271            0 :             default:
    3272            0 :                 break;
    3273              :             }
    3274              : 
    3275              :             // ECONOMIZER - outdoor air humidity ratio sensor offset. really needed ???
    3276            0 :             switch (iEco) {
    3277            0 :             case EconoOp::FixedDewPointAndDryBulb:
    3278              :             case EconoOp::ElectronicEnthalpy: {
    3279            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::HumiditySensorOffset_OutdoorAir) {
    3280              :                     // FaultModel:HumiditySensorOffset:OutdoorAir
    3281            0 :                     thisOAController.OAHumRat += rOffset;
    3282            0 :                     thisOAController.InletHumRat += rOffset;
    3283              :                 }
    3284            0 :             } break;
    3285            0 :             default:
    3286            0 :                 break;
    3287              :             }
    3288              : 
    3289              :             // ECONOMIZER - outdoor air enthalpy sensor offset
    3290            0 :             switch (iEco) {
    3291            0 :             case EconoOp::FixedEnthalpy:
    3292              :             case EconoOp::ElectronicEnthalpy:
    3293              :             case EconoOp::DifferentialDryBulbAndEnthalpy: {
    3294            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::EnthalpySensorOffset_OutdoorAir) {
    3295              :                     // FaultModel:EnthalpySensorOffset:OutdoorAir
    3296            0 :                     thisOAController.OAEnth += rOffset;
    3297            0 :                     thisOAController.InletEnth += rOffset;
    3298              :                 }
    3299            0 :             } break;
    3300            0 :             default:
    3301            0 :                 break;
    3302              :             }
    3303              : 
    3304              :             // ECONOMIZER - return air dry-bulb temperature sensor offset
    3305            0 :             switch (iEco) {
    3306            0 :             case EconoOp::DifferentialDryBulb:
    3307              :             case EconoOp::DifferentialDryBulbAndEnthalpy: {
    3308            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::TemperatureSensorOffset_ReturnAir) {
    3309              :                     // FaultModel:TemperatureSensorOffset:ReturnAir
    3310            0 :                     thisOAController.RetTemp += rOffset;
    3311              :                 }
    3312            0 :             } break;
    3313            0 :             default:
    3314            0 :                 break;
    3315              :             }
    3316              : 
    3317              :             // ECONOMIZER - return air enthalpy sensor offset
    3318            0 :             switch (iEco) {
    3319            0 :             case EconoOp::ElectronicEnthalpy:
    3320              :             case EconoOp::DifferentialDryBulbAndEnthalpy: {
    3321            0 :                 if (state.dataFaultsMgr->FaultsEconomizer(j).type == FaultType::EnthalpySensorOffset_ReturnAir) {
    3322              :                     // FaultModel:EnthalpySensorOffset:ReturnAir
    3323            0 :                     thisOAController.RetEnth += rOffset;
    3324              :                 }
    3325            0 :             } break;
    3326            0 :             default:
    3327            0 :                 break;
    3328              :             }
    3329              :         }
    3330              :     }
    3331              : 
    3332        78423 :     if (ErrorsFound) {
    3333            0 :         ShowFatalError(state, format("Error in {}; program terminated", CurrentModuleObjects[static_cast<int>(CMO::OAController)]));
    3334              :     }
    3335        78423 : } // namespace MixedAir
    3336              : 
    3337       383145 : void OAMixerProps::InitOAMixer(EnergyPlusData &state)
    3338              : {
    3339              :     // SUBROUTINE INFORMATION:
    3340              :     //       AUTHOR         Fred Buhl
    3341              :     //       DATE WRITTEN   Oct 1998
    3342              : 
    3343              :     // PURPOSE OF THIS SUBROUTINE
    3344              :     // Initialize the OAMixer data structure with input node data
    3345              : 
    3346       383145 :     int RetNode = this->RetNode;
    3347       383145 :     int InletNode = this->InletNode;
    3348       383145 :     int RelNode = this->RelNode;
    3349              : 
    3350              :     // Return air stream data
    3351       383145 :     this->RetTemp = state.dataLoopNodes->Node(RetNode).Temp;
    3352       383145 :     this->RetHumRat = state.dataLoopNodes->Node(RetNode).HumRat;
    3353       383145 :     this->RetEnthalpy = state.dataLoopNodes->Node(RetNode).Enthalpy;
    3354       383145 :     this->RetPressure = state.dataLoopNodes->Node(RetNode).Press;
    3355       383145 :     this->RetMassFlowRate = state.dataLoopNodes->Node(RetNode).MassFlowRate;
    3356              :     // Outside air stream data
    3357       383145 :     this->OATemp = state.dataLoopNodes->Node(InletNode).Temp;
    3358       383145 :     this->OAHumRat = state.dataLoopNodes->Node(InletNode).HumRat;
    3359       383145 :     this->OAEnthalpy = state.dataLoopNodes->Node(InletNode).Enthalpy;
    3360       383145 :     this->OAPressure = state.dataLoopNodes->Node(InletNode).Press;
    3361       383145 :     this->OAMassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    3362              :     // Relief air data
    3363       383145 :     this->RelMassFlowRate = state.dataLoopNodes->Node(RelNode).MassFlowRate;
    3364       383145 : }
    3365              : 
    3366        78438 : void OAControllerProps::CalcOAController(EnergyPlusData &state, int const AirLoopNum, bool const FirstHVACIteration)
    3367              : {
    3368              : 
    3369              :     // SUBROUTINE INFORMATION:
    3370              :     //       AUTHOR         Fred Buhl
    3371              :     //       DATE WRITTEN   Oct 1998
    3372              :     //       MODIFIED       Shirey/Raustad FSEC, June 2003
    3373              :     //                      Tianzhen Hong, Feb 2009 for new DCV
    3374              :     //                      Brent Griffith ,EMS override of OA rate
    3375              :     //                      Mangesh Basarkar, 06/2011: Modifying outside air calculation based on DCV flag
    3376              :     //                      Chandan Sharma, FSEC, 25Aug 2011 - Added ProportionalControl
    3377              :     //                           to enhance CO2 based DCV control
    3378              :     //                      Tianzhen Hong, March 2012, zone maximum OA fraction - a TRACE feature
    3379              :     //                      Tianzhen Hong, March 2012, multi-path VRP based on ASHRAE 62.1-2010
    3380              : 
    3381              :     // PURPOSE OF THIS SUBROUTINE
    3382              :     // Determine the outside air flow
    3383              : 
    3384              :     // REFERENCES:
    3385              :     // DOE-2.1E Supplement pages 3.97 - 3.100
    3386              :     // BLAST User Reference pages 183 - 186
    3387              :     // ASHRAE Standard 62.1-2010
    3388              : 
    3389              :     // SUBROUTINE PARAMETER DEFINITIONS:
    3390              :     static constexpr std::string_view RoutineName("CalcOAController: ");
    3391              : 
    3392              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3393        78438 :     Real64 OASignal = 0.0;                  // Outside air flow rate fraction (0.0 to 1.0)
    3394        78438 :     bool AirLoopCyclingFan = false;         // Type of air loop fan (TRUE if Fan:OnOff)
    3395        78438 :     bool HighHumidityOperationFlag = false; // TRUE if zone humidistat senses a high humidity condition
    3396              : 
    3397        78438 :     if (AirLoopNum > 0) {
    3398        78437 :         AirLoopCyclingFan = (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).fanOp == HVAC::FanOp::Cycling);
    3399              :     } else {
    3400            1 :         AirLoopCyclingFan = false;
    3401              :     }
    3402              : 
    3403        78438 :     this->OALimitingFactor = OALimitFactor::None; // oa controller limiting factor
    3404              : 
    3405              :     // Check for no flow
    3406        78438 :     if (this->MixMassFlow <= HVAC::SmallMassFlow) {
    3407              : 
    3408         7595 :         this->OAMassFlow = 0.0;     // outside air mass flow rate
    3409         7595 :         this->RelMassFlow = 0.0;    // relief air mass flow rate
    3410         7595 :         this->MixMassFlow = 0.0;    // mixed air mass flow rate
    3411         7595 :         this->MinOAFracLimit = 0.0; // minimum OA fraction limit
    3412              : 
    3413         7595 :         this->EconomizerStatus = 0;                                                    // economizer status for reporting
    3414         7595 :         this->HeatRecoveryBypassStatus = 0;                                            // HR bypass status for reporting
    3415         7595 :         this->HRHeatingCoilActive = 0;                                                 // resets report variable
    3416         7595 :         this->MixedAirTempAtMinOAFlow = state.dataLoopNodes->Node(this->RetNode).Temp; // track return T
    3417         7595 :         this->HighHumCtrlStatus = 0;                                                   // high humidity control status for reporting
    3418         7595 :         this->OAFractionRpt = 0.0;                                                     // actual OA fraction for reporting
    3419              : 
    3420         7595 :         this->EconoActive = false;       // DataAirLoop variable (OA Controllers)
    3421         7595 :         this->HighHumCtrlActive = false; // DataAirLoop variable (OA Controllers)
    3422              : 
    3423              :         // also reset air loop data for use by other routines
    3424         7595 :         if (AirLoopNum > 0) {
    3425         7594 :             auto &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
    3426         7594 :             auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
    3427              : 
    3428         7594 :             curAirLoopControlInfo.EconoActive = false;        // DataAirLoop variable (AirloopHVAC)
    3429         7594 :             curAirLoopControlInfo.HeatRecoveryBypass = false; // DataAirLoop variable (AirloopHVAC)
    3430         7594 :             curAirLoopControlInfo.HighHumCtrlActive = false;  // DataAirLoop variable (AirloopHVAC)
    3431         7594 :             curAirLoopControlInfo.ResimAirLoopFlag = false;   // DataAirLoop variable (AirloopHVAC)
    3432         7594 :             curAirLoopFlow.OAFrac = 0.0;                      // DataAirLoop variable (AirloopHVAC)
    3433         7594 :             curAirLoopFlow.OAMinFrac = 0.0;                   // DataAirLoop variable (AirloopHVAC)
    3434         7594 :             curAirLoopFlow.MinOutAir = 0.0;
    3435         7594 :             curAirLoopFlow.OAFlow = 0.0;
    3436              :         }
    3437         7595 :         return;
    3438              :     }
    3439              : 
    3440        70843 :     Real64 OutAirMinFrac = 0.0; // Local variable used to calculate min OA fraction
    3441        70843 :     if (AirLoopNum > 0) {
    3442        70843 :         auto const &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
    3443        70843 :         if (curAirLoopFlow.DesSupply >= HVAC::SmallAirVolFlow) {
    3444        70843 :             OutAirMinFrac = this->MinOAMassFlowRate / curAirLoopFlow.DesSupply;
    3445              :         }
    3446              :     } else {
    3447            0 :         if (this->MaxOA >= HVAC::SmallAirVolFlow) {
    3448            0 :             OutAirMinFrac = this->MinOA / this->MaxOA;
    3449              :         }
    3450              :     }
    3451        70843 :     Real64 MinOASchedVal = 1.0; // value of the minimum outside air schedule
    3452        70843 :     if (this->minOASched != nullptr) {
    3453        28119 :         MinOASchedVal = this->minOASched->getCurrentVal();
    3454        28119 :         MinOASchedVal = min(max(MinOASchedVal, 0.0), 1.0);
    3455        28119 :         OutAirMinFrac *= MinOASchedVal;
    3456        28119 :         this->OALimitingFactor = OALimitFactor::Limits;
    3457              :     }
    3458              : 
    3459              :     // Get outside air mass flow rate calculated by mechanical ventilation object [kg/s]
    3460        70843 :     Real64 MechVentOutsideAirMinFrac = 0.0; // fraction of OA specified by mechanical ventilation object
    3461        70843 :     if (AirLoopNum > 0 && this->VentMechObjectNum != 0) {
    3462        17967 :         auto const &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
    3463        17967 :         auto const &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
    3464              : 
    3465              :         // Get system supply air flow rate
    3466        17967 :         Real64 SysSA = 0.0; // System supply air mass flow rate [kg/s]
    3467        17967 :         if (curAirLoopControlInfo.LoopFlowRateSet) {
    3468              :             // if flow rate has been specified by a manager, set it to the specified value
    3469              :             // DesSupply and SupFlow are mass flow rate in kg/s
    3470            8 :             SysSA = curAirLoopFlow.ReqSupplyFrac * curAirLoopFlow.DesSupply;
    3471              :         } else {
    3472        17959 :             SysSA = curAirLoopFlow.SupFlow;
    3473              :         }
    3474              : 
    3475        17967 :         this->MechVentOAMassFlowRequest = state.dataMixedAir->VentilationMechanical(this->VentMechObjectNum).CalcMechVentController(state, SysSA);
    3476        17967 :         MechVentOutsideAirMinFrac = this->MechVentOAMassFlowRequest / curAirLoopFlow.DesSupply;
    3477        17967 :         if (curAirLoopFlow.FanPLR > 0.0) {
    3478        17967 :             MechVentOutsideAirMinFrac *= curAirLoopFlow.FanPLR;
    3479        17967 :             this->MechVentOAMassFlowRequest *= curAirLoopFlow.FanPLR;
    3480              :         }
    3481        17967 :     } else {
    3482        52876 :         this->MechVentOAMassFlowRequest = 0.0;
    3483              :     }
    3484              :     //****** use greater of Mechanical Ventilation Outside Air fraction and OutAirMinFrac
    3485        70843 :     if ((MechVentOutsideAirMinFrac > 0.0) && (OutAirMinFrac > MechVentOutsideAirMinFrac)) {
    3486            1 :         if (!state.dataGlobal->WarmupFlag) {
    3487            1 :             if (this->CountMechVentFrac == 0) {
    3488            1 :                 ++this->CountMechVentFrac;
    3489            2 :                 ShowWarningError(
    3490              :                     state,
    3491            2 :                     format("{}Minimum OA fraction > Mechanical Ventilation Controller request for Controller:OutdoorAir={}, Min OA fraction is used.",
    3492              :                            RoutineName,
    3493            1 :                            this->Name));
    3494            2 :                 ShowContinueError(state,
    3495              :                                   "This may be overriding desired ventilation controls. Check inputs for Minimum Outdoor Air Flow Rate, Minimum "
    3496              :                                   "Outdoor Air Schedule Name and Controller:MechanicalVentilation");
    3497            2 :                 ShowContinueErrorTimeStamp(
    3498            2 :                     state, format("Minimum OA fraction = {:.4R}, Mech Vent OA fraction = {:.4R}", OutAirMinFrac, MechVentOutsideAirMinFrac));
    3499              :             } else {
    3500            0 :                 ShowRecurringWarningErrorAtEnd(state,
    3501            0 :                                                "Controller:OutdoorAir=\"" + this->Name +
    3502              :                                                    "\": Min OA fraction > Mechanical ventilation OA fraction, continues...",
    3503            0 :                                                this->IndexMechVentFrac,
    3504              :                                                OutAirMinFrac,
    3505              :                                                OutAirMinFrac);
    3506              :             }
    3507              :         }
    3508              :     }
    3509        70843 :     if (MechVentOutsideAirMinFrac > OutAirMinFrac) {
    3510        16468 :         OutAirMinFrac = MechVentOutsideAirMinFrac;
    3511        16468 :         this->OALimitingFactor = OALimitFactor::DCV;
    3512              :     }
    3513              : 
    3514        70843 :     OutAirMinFrac = min(max(OutAirMinFrac, 0.0), 1.0);
    3515              : 
    3516              :     // At this point, OutAirMinFrac is still based on AirLoopFlow.DesSupply
    3517        70843 :     if (AirLoopNum > 0) {
    3518        70843 :         auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
    3519              : 
    3520        70843 :         curAirLoopFlow.MinOutAir = OutAirMinFrac * curAirLoopFlow.DesSupply;
    3521              : 
    3522              :         // calculate mixed air temp at min OA flow rate
    3523        70843 :         Real64 ReliefMassFlowAtMinOA = max(curAirLoopFlow.MinOutAir - this->ExhMassFlow, 0.0);
    3524        70843 :         Real64 RecircMassFlowRateAtMinOAFlow = max(state.dataLoopNodes->Node(this->RetNode).MassFlowRate - ReliefMassFlowAtMinOA, 0.0);
    3525        70843 :         if ((RecircMassFlowRateAtMinOAFlow + curAirLoopFlow.MinOutAir) > 0.0) {
    3526        70843 :             Real64 RecircTemp = state.dataLoopNodes->Node(this->RetNode).Temp; // return air temp used for custom economizer control calculation
    3527        70843 :             this->MixedAirTempAtMinOAFlow =
    3528        70843 :                 (RecircMassFlowRateAtMinOAFlow * RecircTemp + curAirLoopFlow.MinOutAir * state.dataLoopNodes->Node(this->OANode).Temp) /
    3529        70843 :                 (RecircMassFlowRateAtMinOAFlow + curAirLoopFlow.MinOutAir);
    3530              :         } else {
    3531            0 :             this->MixedAirTempAtMinOAFlow = state.dataLoopNodes->Node(this->RetNode).Temp;
    3532              :         }
    3533              :     }
    3534              : 
    3535              :     // Economizer
    3536        70843 :     this->CalcOAEconomizer(state, AirLoopNum, OutAirMinFrac, OASignal, HighHumidityOperationFlag, FirstHVACIteration);
    3537              : 
    3538        70843 :     this->OAMassFlow = OASignal * this->MixMassFlow;
    3539              : 
    3540              :     // Do not allow OA to be below Ventilation:Mechanical flow rate or above mixed mass flow rate
    3541        70843 :     if (AirLoopNum > 0 && VentMechObjectNum != 0) {
    3542        17967 :         if (this->MechVentOAMassFlowRequest > this->OAMassFlow) {
    3543         2103 :             this->OAMassFlow = min(this->MechVentOAMassFlowRequest, this->MixMassFlow);
    3544              :         }
    3545              :     }
    3546              : 
    3547              :     // Do not allow OA to be below Exh for controller:outside air
    3548        70843 :     if (this->ControllerType == MixedAirControllerType::ControllerOutsideAir) {
    3549        70843 :         if (this->ExhMassFlow > this->OAMassFlow) {
    3550            2 :             this->OAMassFlow = this->ExhMassFlow;
    3551            2 :             this->OALimitingFactor = OALimitFactor::Exhaust;
    3552              :         }
    3553              :     }
    3554              : 
    3555              :     // if fixed minimum, don't let go below min OA
    3556        70843 :     if (this->FixedMin) {
    3557              :         // cycling fans allow "average" min OA to be below minimum
    3558        48868 :         if (!AirLoopCyclingFan) {
    3559        48868 :             Real64 minOASchedMassFlowRate = this->MinOAMassFlowRate * MinOASchedVal;
    3560        48868 :             if (minOASchedMassFlowRate > this->OAMassFlow) {
    3561         6032 :                 this->OAMassFlow = minOASchedMassFlowRate;
    3562         6032 :                 this->OALimitingFactor = OALimitFactor::Limits;
    3563              :             }
    3564              :         }
    3565              :     }
    3566              : 
    3567              :     // Apply Minimum Fraction of Outdoor Air Schedule
    3568        70843 :     if (this->minOAflowSched != nullptr) {
    3569            0 :         Real64 MinOAflowfracVal = this->minOAflowSched->getCurrentVal();
    3570            0 :         MinOAflowfracVal = min(max(MinOAflowfracVal, 0.0), 1.0);
    3571            0 :         OutAirMinFrac = max(MinOAflowfracVal, OutAirMinFrac);
    3572            0 :         Real64 minOAFracMassFlowRate = this->MixMassFlow * MinOAflowfracVal;
    3573            0 :         if (minOAFracMassFlowRate > this->OAMassFlow) {
    3574            0 :             this->OAMassFlow = minOAFracMassFlowRate;
    3575            0 :             this->OALimitingFactor = OALimitFactor::Limits;
    3576              :         }
    3577              :     }
    3578              : 
    3579              :     // Apply Maximum Fraction of Outdoor Air Schedule
    3580        70843 :     Real64 currentMaxOAMassFlowRate = this->MaxOAMassFlowRate;
    3581        70843 :     if (this->maxOAflowSched != nullptr) {
    3582            0 :         Real64 MaxOAflowfracVal = this->maxOAflowSched->getCurrentVal();
    3583            0 :         MaxOAflowfracVal = min(max(MaxOAflowfracVal, 0.0), 1.0);
    3584            0 :         currentMaxOAMassFlowRate = min(this->MaxOAMassFlowRate, this->MixMassFlow * MaxOAflowfracVal);
    3585            0 :         OutAirMinFrac = min(MaxOAflowfracVal, OutAirMinFrac);
    3586            0 :         if (currentMaxOAMassFlowRate < this->OAMassFlow) {
    3587            0 :             this->OAMassFlow = currentMaxOAMassFlowRate;
    3588            0 :             this->OALimitingFactor = OALimitFactor::Limits;
    3589              :         }
    3590              :     }
    3591              : 
    3592              :     // Don't let the OA flow be > than the max OA limit. OA for high humidity control is allowed to be greater than max OA.
    3593              :     // Night Ventilation has priority and may override an OASignal > 1 high humidity condition with OASignal = 1
    3594        70843 :     if (HighHumidityOperationFlag) {
    3595            0 :         Real64 maxOAMassFlow = this->MaxOAMassFlowRate * max(1.0, OASignal);
    3596            0 :         if (maxOAMassFlow < this->OAMassFlow) {
    3597            0 :             this->OAMassFlow = maxOAMassFlow;
    3598            0 :             this->OALimitingFactor = OALimitFactor::Limits;
    3599              :         }
    3600              :     } else {
    3601        70843 :         if (this->MaxOAMassFlowRate < this->OAMassFlow) {
    3602         1180 :             this->OAMassFlow = this->MaxOAMassFlowRate;
    3603         1180 :             this->OALimitingFactor = OALimitFactor::Limits;
    3604              :         }
    3605              :     }
    3606              : 
    3607        70843 :     if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) && (this->OAMassFlow > this->DemandLimitFlowRate)) {
    3608            0 :         this->OAMassFlow = this->DemandLimitFlowRate;
    3609            0 :         this->OALimitingFactor = OALimitFactor::DemandLimit;
    3610              :     }
    3611        70843 :     if (this->EMSOverrideOARate) {
    3612            0 :         this->OAMassFlow = this->EMSOARateValue;
    3613            0 :         this->OALimitingFactor = OALimitFactor::EMS;
    3614              :     }
    3615              : 
    3616              :     // Don't let OA flow be > mixed air flow.
    3617              :     // Seems if RAB (return air bypass) that this should be don't let OA flow be > design supply flow but that causes other issues
    3618        70843 :     if (this->MixMassFlow < this->OAMassFlow) {
    3619         1938 :         this->OAMassFlow = this->MixMassFlow;
    3620         1938 :         this->OALimitingFactor = OALimitFactor::MixedAir;
    3621              :     }
    3622              : 
    3623              :     // save the min outside air flow fraction and max outside air mass flow rate
    3624        70843 :     if (AirLoopNum > 0) {
    3625        70843 :         auto &curAirLoopControlInfo(state.dataAirLoop->AirLoopControlInfo(AirLoopNum));
    3626        70843 :         auto &curAirLoopFlow(state.dataAirLoop->AirLoopFlow(AirLoopNum));
    3627              : 
    3628        70843 :         curAirLoopFlow.OAMinFrac = OutAirMinFrac;
    3629        70843 :         if (this->FixedMin) {
    3630        48868 :             curAirLoopFlow.MinOutAir = min(OutAirMinFrac * curAirLoopFlow.DesSupply, this->MixMassFlow);
    3631              :         } else {
    3632        21975 :             curAirLoopFlow.MinOutAir = OutAirMinFrac * this->MixMassFlow;
    3633              :         }
    3634        70843 :         if (this->MixMassFlow > 0.0) {
    3635        70843 :             curAirLoopFlow.OAFrac = this->OAMassFlow / this->MixMassFlow;
    3636        70843 :             curAirLoopFlow.OAFlow = this->OAMassFlow;
    3637              :         } else {
    3638            0 :             curAirLoopFlow.OAFrac = 0.0;
    3639            0 :             curAirLoopFlow.OAFlow = 0.0;
    3640              :         }
    3641        70843 :         this->MinOAFracLimit = OutAirMinFrac;
    3642        70843 :         if (HighHumidityOperationFlag && OASignal > 1.0) {
    3643            0 :             curAirLoopFlow.MaxOutAir = this->MaxOAMassFlowRate * OASignal;
    3644              :         } else {
    3645        70843 :             curAirLoopFlow.MaxOutAir = currentMaxOAMassFlowRate;
    3646              :         }
    3647              : 
    3648              :         // MJW - Not sure if this is necessary but keeping it for now
    3649        70843 :         if (curAirLoopControlInfo.HeatingActiveFlag && curAirLoopControlInfo.EconomizerFlowLocked) {
    3650              :             // The airloop needs to be simulated again so that the heating coil & HX can be resimulated
    3651            1 :             if (curAirLoopControlInfo.HeatRecoveryResimFlag && curAirLoopControlInfo.OASysComponentsSimulated) {
    3652            0 :                 curAirLoopControlInfo.ResimAirLoopFlag = true;
    3653            0 :                 curAirLoopControlInfo.HeatRecoveryResimFlag = false;
    3654            0 :                 curAirLoopControlInfo.HeatRecoveryResimFlag2 = true;
    3655              :                 // on the first iteration, air loop heating coils have not be simulated so HeatingCoilActive=FALSE
    3656              :                 // on the second iteration, the heating coils could have been on, but logic tests here could deactivate heating coil
    3657              :                 // reset heating coil active status and HX since logic tests may turn off heating coil
    3658              :                 // the ResimAirLoopFlag will force another iteration and things should line up on subsequent iterations
    3659            0 :                 curAirLoopControlInfo.HeatingActiveFlag = false;
    3660            0 :                 this->HRHeatingCoilActive = 0;
    3661            0 :                 curAirLoopControlInfo.HeatRecoveryBypass = true;
    3662            0 :                 this->HeatRecoveryBypassStatus = 1;
    3663            1 :             } else if (curAirLoopControlInfo.HeatRecoveryResimFlag2) {
    3664            0 :                 curAirLoopControlInfo.ResimAirLoopFlag = true;
    3665            0 :                 curAirLoopControlInfo.HeatRecoveryResimFlag2 = false;
    3666              :             } else {
    3667            1 :                 curAirLoopControlInfo.ResimAirLoopFlag = false;
    3668              :             }
    3669        70842 :         } else if (curAirLoopControlInfo.HeatingActiveFlag) {
    3670         8113 :             this->HRHeatingCoilActive = 1;
    3671              :         } else {
    3672        62729 :             this->HRHeatingCoilActive = 0;
    3673              :         }
    3674              : 
    3675              :     } // if (AirLoopNum > 0)
    3676              : 
    3677              :     // Set the relief air flow rate (must be done last to account for changes in OAMassFlow
    3678        70843 :     this->RelMassFlow = max(this->OAMassFlow - this->ExhMassFlow, 0.0);
    3679              : 
    3680              :     // Save OA fraction for reporting
    3681        70843 :     if (this->MixMassFlow > 0) {
    3682        70843 :         this->OAFractionRpt = this->OAMassFlow / this->MixMassFlow;
    3683              :     } else {
    3684            0 :         if (this->OAMassFlow > 0) {
    3685            0 :             this->OAFractionRpt = OASignal;
    3686              :         } else {
    3687            0 :             this->OAFractionRpt = 0.0;
    3688              :         }
    3689              :     }
    3690        70843 :     this->RelTemp = this->RetTemp;
    3691        70843 :     this->RelEnth = this->RetEnth;
    3692        70843 :     this->RelSensiLossRate =
    3693        70843 :         this->RelMassFlow * Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat) * (this->RelTemp - state.dataEnvrn->OutDryBulbTemp);
    3694        70843 :     this->RelTotalLossRate = this->RelMassFlow * (this->RelEnth - state.dataEnvrn->OutEnthalpy);
    3695        70843 :     this->RelLatentLossRate = this->RelTotalLossRate - this->RelSensiLossRate;
    3696        70843 :     this->OALimitingFactorReport = static_cast<int>(OALimitingFactor);
    3697              : }
    3698              : 
    3699        17979 : Real64 VentilationMechanicalProps::CalcMechVentController(EnergyPlusData &state,
    3700              :                                                           Real64 SysSA // System supply air mass flow rate [kg/s]
    3701              : )
    3702              : {
    3703              :     static constexpr std::string_view RoutineName("CalcMechVentController: ");
    3704              :     static std::string_view const &CurrentModuleObject(CurrentModuleObjects[static_cast<int>(CMO::MechVentilation)]);
    3705              : 
    3706              :     // new local variables for DCV
    3707              :     // Zone OA flow rate based on each calculation method [m3/s]
    3708        17979 :     std::array<Real64, static_cast<int>(DataSizing::OAFlowCalcMethod::Num)> ZoneOACalc{0.0};
    3709              :     Real64 ZoneOABZ;         // Zone breathing-zone OA flow rate [m3/s]
    3710              :     Real64 ZoneOA;           // Zone OA flow rate [m3/s]
    3711              :     Real64 ZoneOAFrac;       // Zone OA fraction (as a fraction of actual supply air flow rate)
    3712              :     Real64 SysOAuc;          // System uncorrected OA flow rate
    3713              :     Real64 SysOA;            // System supply OA volume flow rate [m3/s]
    3714              :     Real64 SysEv;            // System ventilation efficiency
    3715              :     Real64 NodeTemp;         // node temperature
    3716              :     Real64 NodeHumRat;       // node humidity ratio
    3717        17979 :     Real64 ZoneMaxCO2 = 0.0; // Breathing-zone CO2 concentration
    3718        17979 :     Real64 ZoneMinCO2 = 0.0; // Minimum CO2 concentration in zone
    3719        17979 :     Real64 ZoneOAMin = 0.0;  // Minimum Zone OA flow rate when the zone is unoccupied (i.e. ZoneOAPeople = 0)
    3720        17979 :     Real64 ZoneOAMax = 0.0;  // Maximum Zone OA flow rate (ZoneOAPeople + ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)])
    3721        17979 :     Real64 MechVentOAMassFlow = 0.0;
    3722              : 
    3723              :     // Apply mechanical ventilation only when it is available/allowed
    3724        17979 :     if (this->availSched->getCurrentVal() > 0) {
    3725        17979 :         Real64 SysOAMassFlow = 0.0; // System supply OA mass flow rate [kg/s]
    3726        17979 :         if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQP) {
    3727              :             // IAQP for CO2 control
    3728            3 :             for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3729            2 :                 auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3730            2 :                 int ZoneNum = thisMechVentZone.zoneNum;
    3731            2 :                 SysOAMassFlow +=
    3732            2 :                     state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP * thisMechVentZone.zoneOASched->getCurrentVal();
    3733              :             }
    3734            1 :             MechVentOAMassFlow = SysOAMassFlow;
    3735        17978 :         } else if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQPGC) {
    3736              :             // IAQP for generic contaminant control
    3737            3 :             for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3738            2 :                 auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3739            2 :                 int ZoneNum = thisMechVentZone.zoneNum;
    3740            2 :                 SysOAMassFlow +=
    3741            2 :                     state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToGCSP * thisMechVentZone.zoneOASched->getCurrentVal();
    3742              :             }
    3743            1 :             MechVentOAMassFlow = SysOAMassFlow;
    3744        17977 :         } else if (this->SystemOAMethod == DataSizing::SysOAMethod::IAQPCOM) {
    3745              :             // IAQP for both CO2 and generic contaminant control
    3746            2 :             SysOAMassFlow = 0.0;
    3747            6 :             for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3748            4 :                 auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3749            4 :                 int ZoneNum = thisMechVentZone.zoneNum;
    3750            4 :                 SysOAMassFlow +=
    3751            4 :                     state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToCO2SP * thisMechVentZone.zoneOASched->getCurrentVal();
    3752              :             }
    3753            2 :             MechVentOAMassFlow = SysOAMassFlow;
    3754            2 :             SysOAMassFlow = 0.0;
    3755            6 :             for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3756            4 :                 auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3757            4 :                 int ZoneNum = thisMechVentZone.zoneNum;
    3758            4 :                 SysOAMassFlow +=
    3759            4 :                     state.dataContaminantBalance->ZoneSysContDemand(ZoneNum).OutputRequiredToGCSP * thisMechVentZone.zoneOASched->getCurrentVal();
    3760              :             }
    3761            2 :             MechVentOAMassFlow = max(SysOAMassFlow, MechVentOAMassFlow);
    3762              :         } else {
    3763              :             // for system OA methods: Zone_Sum, VRP, CO2 methods
    3764              :             // new code for DCV method complying with the VRP defined in ASHRAE Standard 62.1-2010
    3765              : 
    3766              :             // Loop through each zone first to calc uncorrected system OA flow rate
    3767        17975 :             SysOAuc = 0.0;
    3768        17975 :             SysOA = 0.0;
    3769        51836 :             for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3770        33861 :                 auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3771        33861 :                 int ZoneNum = thisMechVentZone.zoneNum;
    3772        33861 :                 auto const &curZone(state.dataHeatBal->Zone(ZoneNum));
    3773        33861 :                 Real64 multiplier = curZone.Multiplier * curZone.ListMultiplier * thisMechVentZone.zoneOASched->getCurrentVal();
    3774              : 
    3775              :                 // Calc the zone OA flow rate based on the people component
    3776              :                 // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
    3777              :                 //  Checking DCV flag before calculating zone OA per person
    3778        33861 :                 if (this->DCVFlag && this->SystemOAMethod != DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    3779        31742 :                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
    3780        31742 :                         state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * thisMechVentZone.ZoneOAPeopleRate;
    3781              :                 } else {
    3782         2119 :                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] = curZone.TotOccupants * thisMechVentZone.ZoneOAPeopleRate;
    3783              :                 }
    3784              : 
    3785              :                 // Calc the zone OA flow rate based on the floor area component
    3786        33861 :                 ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] = curZone.FloorArea * thisMechVentZone.ZoneOAAreaRate;
    3787        33861 :                 ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] = thisMechVentZone.ZoneOAFlowRate;
    3788        33861 :                 ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)] = (thisMechVentZone.ZoneOAACHRate * curZone.Volume) / 3600.0;
    3789              : 
    3790              :                 // Calc the breathing-zone OA flow rate
    3791        33861 :                 int OAIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex; // index to design specification outdoor air objects
    3792        33861 :                 if (OAIndex > 0) {
    3793        33861 :                     switch (state.dataSize->OARequirements(OAIndex).OAFlowMethod) {
    3794        33829 :                     case DataSizing::OAFlowCalcMethod::Sum: {
    3795        33829 :                         ZoneOABZ = multiplier * (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] +
    3796        33829 :                                                  ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
    3797        33829 :                                                  ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] +
    3798        33829 :                                                  ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
    3799        33829 :                     } break;
    3800            7 :                     case DataSizing::OAFlowCalcMethod::Max: {
    3801            7 :                         ZoneOABZ = multiplier * max(ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)],
    3802            7 :                                                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)],
    3803            7 :                                                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)],
    3804            7 :                                                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
    3805              : 
    3806            7 :                     } break;
    3807           25 :                     default: {
    3808           25 :                         ZoneOABZ = multiplier * ZoneOACalc[static_cast<int>(state.dataSize->OARequirements(OAIndex).OAFlowMethod)];
    3809           25 :                         break;
    3810              :                     }
    3811              :                     }
    3812              :                 } else {
    3813            0 :                     ZoneOABZ = multiplier * ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)];
    3814              :                 }
    3815              : 
    3816        33861 :                 if (this->SystemOAMethod == DataSizing::SysOAMethod::ZoneSum) {
    3817              :                     // Sum the zone OA flow rates and done
    3818         2116 :                     SysOA += ZoneOABZ;
    3819              :                 } else {
    3820              :                     // Calc the uncorrected system OA flow rate - VRP and ProportionalControl
    3821        31745 :                     SysOAuc += ZoneOABZ;
    3822              :                 }
    3823              :             }
    3824              : 
    3825              :             // get system supply air flow rate
    3826        17975 :             if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
    3827         2110 :                 this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
    3828         2104 :                 this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate ||
    3829         2102 :                 this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
    3830              : 
    3831              :                 // System supply air flow rate is always greater than or equal the system outdoor air flow rate
    3832        15874 :                 if ((SysSA > 0.0) && (SysSA < (SysOAuc * state.dataEnvrn->StdRhoAir))) {
    3833         2806 :                     SysSA = SysOAuc * state.dataEnvrn->StdRhoAir;
    3834              :                 }
    3835              : 
    3836              :                 // calc Xs - average outdoor air fraction
    3837        15874 :                 if (SysSA > 0.0) {
    3838        15182 :                     Xs = (SysOAuc * state.dataEnvrn->StdRhoAir) / SysSA;
    3839              :                 } else {
    3840          692 :                     Xs = 0.0;
    3841              :                 }
    3842              : 
    3843              :                 // Loop through each zone again
    3844        15874 :                 SysEv = 2.0; // starting with a big fraction
    3845        47619 :                 for (int ZoneIndex = 1; ZoneIndex <= this->NumofVentMechZones; ++ZoneIndex) {
    3846        31745 :                     auto &thisMechVentZone = this->VentMechZone(ZoneIndex);
    3847        31745 :                     int ZoneNum = thisMechVentZone.zoneNum;
    3848        31745 :                     int ZoneEquipConfigNum = ZoneNum; // correspondence - 1:1 of ZoneEquipConfig to Zone index
    3849        31745 :                     Real64 ZoneEz = 0.0;              // Zone air distribution effectiveness
    3850              : 
    3851              :                     // Assign references
    3852        31745 :                     auto &curZone(state.dataHeatBal->Zone(ZoneNum));
    3853        31745 :                     auto &curZoneSysEnergyDemand(state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneEquipConfigNum));
    3854        31745 :                     Real64 multiplier = curZone.Multiplier * curZone.ListMultiplier * thisMechVentZone.zoneOASched->getCurrentVal();
    3855              : 
    3856              :                     // Calc the zone OA flow rate based on the people component
    3857              :                     // ZoneIntGain(ZoneNum)%NOFOCC is the number of occupants of a zone at each time step, already counting the occupant schedule
    3858              :                     //  Checking DCV flag before calculating zone OA per person
    3859        31745 :                     if (this->DCVFlag && this->SystemOAMethod != DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    3860        31724 :                         ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
    3861        31724 :                             state.dataHeatBal->ZoneIntGain(ZoneNum).NOFOCC * multiplier * thisMechVentZone.ZoneOAPeopleRate;
    3862              :                     } else {
    3863           21 :                         ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] =
    3864           21 :                             curZone.TotOccupants * multiplier * thisMechVentZone.ZoneOAPeopleRate;
    3865              :                     }
    3866              : 
    3867              :                     // Calc the zone OA flow rate based on the floor area component
    3868        63490 :                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] =
    3869        31745 :                         curZone.FloorArea * multiplier * thisMechVentZone.ZoneOAAreaRate;
    3870        31745 :                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] = multiplier * thisMechVentZone.ZoneOAFlowRate;
    3871        63490 :                     ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)] =
    3872        31745 :                         multiplier * (thisMechVentZone.ZoneOAACHRate * curZone.Volume) / 3600.0;
    3873              : 
    3874              :                     // Calc the breathing-zone OA flow rate
    3875        31745 :                     int OAIndex = thisMechVentZone.ZoneDesignSpecOAObjIndex;
    3876        31745 :                     if (OAIndex > 0) {
    3877        31745 :                         switch (state.dataSize->OARequirements(OAIndex).OAFlowMethod) {
    3878        31728 :                         case DataSizing::OAFlowCalcMethod::Sum: {
    3879        31728 :                             ZoneOABZ = ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] +
    3880        31728 :                                        ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
    3881        31728 :                                        ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)] +
    3882        31728 :                                        ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)];
    3883        31728 :                         } break;
    3884            4 :                         case DataSizing::OAFlowCalcMethod::Max: {
    3885            4 :                             ZoneOABZ = max(ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)],
    3886            4 :                                            ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)],
    3887            4 :                                            ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerZone)],
    3888            4 :                                            ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::ACH)]);
    3889              : 
    3890            4 :                         } break;
    3891           13 :                         default: {
    3892           13 :                             ZoneOABZ = ZoneOACalc[static_cast<int>(state.dataSize->OARequirements(OAIndex).OAFlowMethod)];
    3893           13 :                             break;
    3894              :                         }
    3895              :                         }
    3896              :                     }
    3897              : 
    3898              :                     // use the ventilation rate procedure in ASHRAE Standard 62.1-2007
    3899              :                     // Calc the zone supplied OA flow rate counting the zone air distribution effectiveness
    3900              :                     //  First check whether the zone air distribution effectiveness schedule exists, if yes uses it;
    3901              :                     //   otherwise uses the inputs of zone distribution effectiveness in cooling mode or heating mode
    3902        31745 :                     if (thisMechVentZone.zoneADEffSched != nullptr) {
    3903              :                         // Get schedule value for the zone air distribution effectiveness
    3904           14 :                         ZoneEz = thisMechVentZone.zoneADEffSched->getCurrentVal();
    3905              :                     } else {
    3906        31731 :                         Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired;
    3907              : 
    3908              :                         // Zone in cooling mode
    3909        31731 :                         if (ZoneLoad < 0.0) {
    3910        16739 :                             ZoneEz = thisMechVentZone.ZoneADEffCooling;
    3911              :                         }
    3912              : 
    3913              :                         // Zone in heating mode
    3914        31731 :                         if (ZoneLoad > 0.0) {
    3915        14907 :                             ZoneEz = thisMechVentZone.ZoneADEffHeating;
    3916              :                         }
    3917              :                     }
    3918        31745 :                     if (ZoneEz <= 0.0) {
    3919              :                         // Enforce defaults
    3920           85 :                         ZoneEz = 1.0;
    3921              :                     }
    3922              : 
    3923              :                     // Calc zone supply OA flow rate
    3924        31745 :                     if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
    3925              :                         // the VRP case
    3926        31731 :                         ZoneOA = ZoneOABZ / ZoneEz;
    3927              : 
    3928           14 :                     } else if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
    3929           14 :                                this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
    3930            2 :                                this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
    3931              :                         // Check whether "Carbon Dioxide Control Availability Schedule" for ZoneControl:ContaminantController is specified
    3932           14 :                         if (curZone.zoneContamControllerSched != nullptr) {
    3933              :                             // Check the availability schedule value for ZoneControl:ContaminantController
    3934           14 :                             Real64 ZoneContamControllerSchedVal = curZone.zoneContamControllerSched->getCurrentVal();
    3935           14 :                             if (ZoneContamControllerSchedVal > 0.0) {
    3936           14 :                                 ZoneOAMin = ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] / ZoneEz;
    3937           14 :                                 ZoneOAMax = (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] +
    3938           14 :                                              ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)]) /
    3939              :                                             ZoneEz;
    3940           14 :                                 if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
    3941            2 :                                     ZoneOAMax = ZoneOABZ / ZoneEz;
    3942            2 :                                     if (thisMechVentZone.oaPropCtlMinRateSched != nullptr) {
    3943            2 :                                         ZoneOAMin = ZoneOAMax * thisMechVentZone.oaPropCtlMinRateSched->getCurrentVal();
    3944              :                                     } else {
    3945            0 :                                         ZoneOAMin = ZoneOAMax;
    3946              :                                     }
    3947            2 :                                     if (ZoneOAMax < ZoneOAMin) {
    3948            0 :                                         ZoneOAMin = ZoneOAMax;
    3949            0 :                                         ++this->OAMaxMinLimitErrorCount;
    3950            0 :                                         if (this->OAMaxMinLimitErrorCount < 2) {
    3951            0 :                                             ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    3952            0 :                                             ShowContinueError(
    3953              :                                                 state,
    3954            0 :                                                 format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum zone "
    3955              :                                                        "outdoor air rate ({:.4R}), is not greater than minimum zone outdoor air rate ({:.4R}).",
    3956              :                                                        ZoneOAMax,
    3957              :                                                        ZoneOAMin));
    3958            0 :                                             ShowContinueError(state,
    3959              :                                                               " The minimum zone outdoor air rate is set to the maximum zone outdoor air rate. "
    3960              :                                                               "Simulation continues...");
    3961            0 :                                             ShowContinueErrorTimeStamp(state, "");
    3962              :                                         } else {
    3963            0 :                                             ShowRecurringWarningErrorAtEnd(
    3964              :                                                 state,
    3965            0 :                                                 format("{} = \"{}\", For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum "
    3966              :                                                        "zone outdoor air rate is not greater than minimum zone outdoor air rate. Error continues...",
    3967              :                                                        CurrentModuleObject,
    3968            0 :                                                        this->Name),
    3969            0 :                                                 this->OAMaxMinLimitErrorIndex);
    3970              :                                         }
    3971              :                                     }
    3972              :                                 }
    3973              : 
    3974           14 :                                 if (ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] > 0.0) {
    3975           14 :                                     if (state.dataContaminantBalance->ZoneCO2GainFromPeople(ZoneNum) > 0.0) {
    3976           14 :                                         if (curZone.zoneMinCO2Sched != nullptr) {
    3977              :                                             // Take the schedule value of "Minimum Carbon Dioxide Concentration Schedule Name"
    3978              :                                             // in the ZoneControl:ContaminantController
    3979            2 :                                             ZoneMinCO2 = curZone.zoneMinCO2Sched->getCurrentVal();
    3980              :                                         } else {
    3981           12 :                                             ZoneMinCO2 = state.dataContaminantBalance->OutdoorCO2;
    3982              :                                         }
    3983              : 
    3984              :                                         // Calculate zone maximum target CO2 concentration in PPM
    3985           14 :                                         if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    3986              :                                             // Accumulate CO2 generation from people at design occupancy and current activity level
    3987           12 :                                             Real64 CO2PeopleGeneration = 0.0;
    3988           24 :                                             for (int const PeopleNum : thisMechVentZone.peopleIndexes) {
    3989           12 :                                                 CO2PeopleGeneration += state.dataHeatBal->People(PeopleNum).NumberOfPeople *
    3990           24 :                                                                        state.dataHeatBal->People(PeopleNum).CO2RateFactor *
    3991           12 :                                                                        state.dataHeatBal->People(PeopleNum).activityLevelSched->getCurrentVal();
    3992           12 :                                             }
    3993           12 :                                             ZoneMaxCO2 = state.dataContaminantBalance->OutdoorCO2 +
    3994           12 :                                                          (CO2PeopleGeneration * curZone.Multiplier * curZone.ListMultiplier * 1.0e6) / ZoneOAMax;
    3995            2 :                                         } else if (curZone.zoneMaxCO2Sched != nullptr) {
    3996            2 :                                             ZoneMaxCO2 = curZone.zoneMaxCO2Sched->getCurrentVal();
    3997              :                                         } else {
    3998            0 :                                             ZoneMaxCO2 = state.dataContaminantBalance->OutdoorCO2 +
    3999            0 :                                                          (state.dataContaminantBalance->ZoneCO2GainFromPeople(ZoneNum) * curZone.Multiplier *
    4000            0 :                                                           curZone.ListMultiplier * 1.0e6) /
    4001              :                                                              ZoneOAMax;
    4002              :                                         }
    4003              : 
    4004           14 :                                         if (ZoneMaxCO2 <= ZoneMinCO2) {
    4005            0 :                                             ++this->CO2MaxMinLimitErrorCount;
    4006            0 :                                             if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
    4007            0 :                                                 if (this->CO2MaxMinLimitErrorCount < 2) {
    4008            0 :                                                     ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    4009            0 :                                                     ShowContinueError(
    4010              :                                                         state,
    4011            0 :                                                         format("For System Outdoor Air Method = ProportionalControlBasedOnOccupancySchedule, "
    4012              :                                                                "maximum target CO2 concentration ({:.2R}), is not greater than minimum target "
    4013              :                                                                "CO2 concentration ({:.2R}).",
    4014              :                                                                ZoneMaxCO2,
    4015              :                                                                ZoneMinCO2));
    4016            0 :                                                     ShowContinueError(state,
    4017              :                                                                       "\"ProportionalControlBasedOnOccupancySchedule\" will not be modeled. "
    4018              :                                                                       "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
    4019              :                                                                       "continues...");
    4020            0 :                                                     ShowContinueErrorTimeStamp(state, "");
    4021              :                                                 } else {
    4022            0 :                                                     ShowRecurringWarningErrorAtEnd(state,
    4023            0 :                                                                                    format("{} = \"{}\", For System Outdoor Air Method = "
    4024              :                                                                                           "ProportionalControlBasedOnOccupancySchedule, maximum "
    4025              :                                                                                           "target CO2 concentration is not greater than minimum "
    4026              :                                                                                           "target CO2 concentration. Error continues...",
    4027              :                                                                                           CurrentModuleObject,
    4028            0 :                                                                                           this->Name),
    4029            0 :                                                                                    this->CO2MaxMinLimitErrorIndex);
    4030              :                                                 }
    4031              :                                             }
    4032            0 :                                             if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    4033            0 :                                                 if (this->CO2MaxMinLimitErrorCount < 2) {
    4034            0 :                                                     ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    4035            0 :                                                     ShowContinueError(
    4036              :                                                         state,
    4037            0 :                                                         format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, "
    4038              :                                                                "maximum target CO2 concentration ({:.2R}), is not greater than minimum target "
    4039              :                                                                "CO2 concentration ({:.2R}).",
    4040              :                                                                ZoneMaxCO2,
    4041              :                                                                ZoneMinCO2));
    4042            0 :                                                     ShowContinueError(state,
    4043              :                                                                       "\"ProportionalControlBasedOnDesignOccupancy\" will not be modeled. "
    4044              :                                                                       "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
    4045              :                                                                       "continues...");
    4046            0 :                                                     ShowContinueErrorTimeStamp(state, "");
    4047              :                                                 } else {
    4048            0 :                                                     ShowRecurringWarningErrorAtEnd(state,
    4049            0 :                                                                                    format("{} = \"{}\", For System Outdoor Air Method = "
    4050              :                                                                                           "ProportionalControlBasedOnDesignOccupancy, maximum "
    4051              :                                                                                           "target CO2 concentration is not greater than minimum "
    4052              :                                                                                           "target CO2 concentration. Error continues...",
    4053              :                                                                                           CurrentModuleObject,
    4054            0 :                                                                                           this->Name),
    4055            0 :                                                                                    this->CO2MaxMinLimitErrorIndex);
    4056              :                                                 }
    4057              :                                             }
    4058            0 :                                             if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
    4059            0 :                                                 if (this->CO2MaxMinLimitErrorCount < 2) {
    4060            0 :                                                     ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    4061            0 :                                                     ShowContinueError(
    4062              :                                                         state,
    4063            0 :                                                         format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOARate, maximum "
    4064              :                                                                "target CO2 concentration ({:.2R}), is not greater than minimum target CO2 "
    4065              :                                                                "concentration ({:.2R}).",
    4066              :                                                                ZoneMaxCO2,
    4067              :                                                                ZoneMinCO2));
    4068            0 :                                                     ShowContinueError(
    4069              :                                                         state,
    4070              :                                                         "\"ProportionalControlBasedOnDesignOARate\" will not be modeled. Default "
    4071              :                                                         "\"Standard62.1VentilationRateProcedure\" will be modeled. Simulation continues...");
    4072            0 :                                                     ShowContinueErrorTimeStamp(state, "");
    4073              :                                                 } else {
    4074            0 :                                                     ShowRecurringWarningErrorAtEnd(state,
    4075            0 :                                                                                    format("{} = \"{}\", For System Outdoor Air Method = "
    4076              :                                                                                           "ProportionalControlBasedOnDesignOARate, maximum target "
    4077              :                                                                                           "CO2 concentration is not greater than minimum target CO2 "
    4078              :                                                                                           "concentration. Error continues...",
    4079              :                                                                                           CurrentModuleObject,
    4080            0 :                                                                                           this->Name),
    4081            0 :                                                                                    this->CO2MaxMinLimitErrorIndex);
    4082              :                                                 }
    4083              :                                             }
    4084              : 
    4085            0 :                                             ZoneOA = ZoneOABZ / ZoneEz;
    4086              :                                         } else {
    4087              : 
    4088           14 :                                             if (state.dataContaminantBalance->ZoneAirCO2(ZoneNum) <= ZoneMinCO2) {
    4089              :                                                 // Zone air CO2 concentration is less than minimum zone CO2 concentration, set the Zone OA flow
    4090              :                                                 // rate to minimum Zone OA flow rate when the zone is unoccupied
    4091            4 :                                                 ZoneOA = ZoneOAMin;
    4092           10 :                                             } else if (state.dataContaminantBalance->ZoneAirCO2(ZoneNum) >= ZoneMaxCO2) {
    4093              :                                                 // Zone air CO2 concentration is greater than maximum zone CO2 concentration, set the Zone OA flow
    4094              :                                                 // rate to maximum Zone OA flow rate (i.e.
    4095              :                                                 // ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerArea)] + ZoneOAPeople)
    4096            4 :                                                 ZoneOA = ZoneOAMax;
    4097              :                                             } else {
    4098              :                                                 // Zone air CO2 concentration is between maximum and minimum limits of zone CO2 concentration,
    4099              :                                                 // set Zone OA flow rate by proportionally adjusting between ZoneOAMin and ZoneOAMax
    4100            6 :                                                 ZoneOA = ZoneOAMin +
    4101            6 :                                                          (ZoneOAMax - ZoneOAMin) * ((state.dataContaminantBalance->ZoneAirCO2(ZoneNum) - ZoneMinCO2) /
    4102            6 :                                                                                     (ZoneMaxCO2 - ZoneMinCO2));
    4103              :                                             }
    4104              :                                         }
    4105              :                                     } else {
    4106            0 :                                         if (state.dataGlobal->DisplayExtraWarnings) {
    4107            0 :                                             ++this->CO2GainErrorCount;
    4108            0 :                                             if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc) {
    4109            0 :                                                 if (this->CO2GainErrorCount < 2) {
    4110            0 :                                                     ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    4111            0 :                                                     ShowContinueError(
    4112              :                                                         state,
    4113            0 :                                                         format("For System Outdoor Air Method = ProportionalControlBasedOnOccupancySchedule, CO2 "
    4114              :                                                                "generation from people is not greater than zero. Occurs in Zone =\"{}\". ",
    4115            0 :                                                                curZone.Name));
    4116            0 :                                                     ShowContinueError(state,
    4117              :                                                                       "\"ProportionalControlBasedOnOccupancySchedule\" will not be modeled. "
    4118              :                                                                       "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
    4119              :                                                                       "continues...");
    4120            0 :                                                     ShowContinueErrorTimeStamp(state, "");
    4121              :                                                 } else {
    4122            0 :                                                     ShowRecurringWarningErrorAtEnd(
    4123              :                                                         state,
    4124            0 :                                                         format("{} = \"{}\", For System Outdoor Air Method = "
    4125              :                                                                "ProportionalControlBasedOnOccupancySchedule, "
    4126              :                                                                "CO2 generation from people is not greater than zero. Error continues...",
    4127              :                                                                CurrentModuleObject,
    4128            0 :                                                                this->Name),
    4129            0 :                                                         this->CO2GainErrorIndex);
    4130              :                                                 }
    4131              :                                             }
    4132            0 :                                             if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc) {
    4133            0 :                                                 if (this->CO2GainErrorCount < 2) {
    4134            0 :                                                     ShowSevereError(state, format("{}{} = \"{}\".", RoutineName, CurrentModuleObject, this->Name));
    4135            0 :                                                     ShowContinueError(
    4136              :                                                         state,
    4137            0 :                                                         format("For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, CO2 "
    4138              :                                                                "generation from people is not greater than zero. Occurs in Zone =\"{}\". ",
    4139            0 :                                                                curZone.Name));
    4140            0 :                                                     ShowContinueError(state,
    4141              :                                                                       "\"ProportionalControlBasedOnDesignOccupancy\" will not be modeled. "
    4142              :                                                                       "Default \"Standard62.1VentilationRateProcedure\" will be modeled. Simulation "
    4143              :                                                                       "continues...");
    4144            0 :                                                     ShowContinueErrorTimeStamp(state, "");
    4145              :                                                 } else {
    4146            0 :                                                     ShowRecurringWarningErrorAtEnd(
    4147              :                                                         state,
    4148            0 :                                                         format(
    4149              :                                                             "{} = \"{}\", For System Outdoor Air Method = ProportionalControlBasedOnDesignOccupancy, "
    4150              :                                                             "CO2 generation from people is not greater than zero. Error continues...",
    4151              :                                                             CurrentModuleObject,
    4152            0 :                                                             this->Name),
    4153            0 :                                                         this->CO2GainErrorIndex);
    4154              :                                                 }
    4155              :                                             }
    4156              :                                         }
    4157            0 :                                         ZoneOA = ZoneOABZ / ZoneEz;
    4158              :                                     }
    4159              :                                 } else {
    4160              :                                     // ZoneOACalc[static_cast<int>(DataSizing::OAFlowCalcMethod::PerPerson)] is less than or equal to zero
    4161            0 :                                     ZoneOA = ZoneOABZ / ZoneEz;
    4162              :                                 }
    4163              :                             } else {
    4164              :                                 // ZoneControl:ContaminantController is scheduled off (not available)
    4165            0 :                                 ZoneOA = ZoneOABZ / ZoneEz;
    4166              :                             }
    4167              :                         } else {
    4168              :                             // "Carbon Dioxide Control Availability Schedule" for ZoneControl:ContaminantController not found
    4169            0 :                             ZoneOA = ZoneOABZ / ZoneEz;
    4170              :                         }
    4171           14 :                         SysOA = SysOA + ZoneOA;
    4172              :                     }
    4173              : 
    4174              :                     // Get the zone supply air flow rate
    4175        31745 :                     Real64 ZoneSA = 0.0; // Zone supply air flow rate
    4176        31745 :                     Real64 ZonePA = 0.0; // Zone primary air flow rate
    4177        31745 :                     Ep = 1.0;
    4178        31745 :                     if (ZoneEquipConfigNum > 0) {
    4179        31745 :                         auto &curZoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(ZoneEquipConfigNum);
    4180        63490 :                         for (int InNodeIndex = 1; InNodeIndex <= curZoneEquipConfig.NumInletNodes; ++InNodeIndex) {
    4181              :                             // Assume primary air is always stored at the AirDistUnitCool (cooling deck if dual duct)
    4182        31745 :                             int PriNode = curZoneEquipConfig.AirDistUnitCool(InNodeIndex).InNode; // primary node of zone terminal unit
    4183        31745 :                             Real64 MassFlowRate = 0.0;
    4184        31745 :                             if (PriNode > 0) {
    4185        31742 :                                 NodeTemp = state.dataLoopNodes->Node(PriNode).Temp;
    4186        31742 :                                 NodeHumRat = state.dataLoopNodes->Node(PriNode).HumRat;
    4187        31742 :                                 MassFlowRate = state.dataLoopNodes->Node(PriNode).MassFlowRate;
    4188              :                             }
    4189              :                             // total primary air to terminal units of the zone
    4190        31745 :                             if (MassFlowRate > 0.0) {
    4191        30341 :                                 ZonePA +=
    4192        30341 :                                     MassFlowRate / Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, NodeTemp, NodeHumRat);
    4193              :                             }
    4194              : 
    4195              :                             // or InletNode = ZoneEquipConfig(ZoneEquipConfigNum)%AirDistUnitCool(InNodeIndex)%OutNode
    4196        31745 :                             int InletNode = curZoneEquipConfig.InletNode(InNodeIndex); // outlet node of zone terminal unit
    4197        31745 :                             MassFlowRate = 0.0;
    4198        31745 :                             if (InletNode > 0) {
    4199        31745 :                                 NodeTemp = state.dataLoopNodes->Node(InletNode).Temp;
    4200        31745 :                                 NodeHumRat = state.dataLoopNodes->Node(InletNode).HumRat; // ZoneAirHumRat(ZoneNum)
    4201        31745 :                                 MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    4202              :                             }
    4203              :                             // total supply air to the zone
    4204        31745 :                             if (MassFlowRate > 0.0) {
    4205        30341 :                                 ZoneSA +=
    4206        30341 :                                     MassFlowRate / Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, NodeTemp, NodeHumRat);
    4207              :                             }
    4208              :                         }
    4209              : 
    4210              :                         // calc zone primary air fraction
    4211        31745 :                         if (ZoneSA > 0.0) {
    4212        30341 :                             Ep = ZonePA / ZoneSA;
    4213              :                         }
    4214        31745 :                         if (Ep > 1.0) {
    4215            0 :                             Ep = 1.0;
    4216              :                         }
    4217              :                     }
    4218              : 
    4219              :                     // Calc the zone OA fraction = Zone OA flow rate / Zone supply air flow rate
    4220        31745 :                     if (ZoneSA > 0.0) {
    4221        30341 :                         ZoneOAFrac = ZoneOA / ZoneSA;
    4222              :                         // Zone OA fraction cannot be more than 1
    4223        30341 :                         if (ZoneOAFrac > 1.0) {
    4224         2834 :                             ZoneOAFrac = 1.0;
    4225              :                         }
    4226              :                     } else {
    4227         1404 :                         ZoneOAFrac = 0.0;
    4228              :                     }
    4229              : 
    4230              :                     // added for TRACE - zone maximum OA fraction - calculate the adjustment factor for the TU/zone supply air flow
    4231              :                     // only for VRP system OA method
    4232        31745 :                     curZoneSysEnergyDemand.SupplyAirAdjustFactor = 1.0;
    4233              : 
    4234        31745 :                     if (this->SystemOAMethod == DataSizing::SysOAMethod::VRP || this->SystemOAMethod == DataSizing::SysOAMethod::VRPL) {
    4235        31731 :                         if (ZoneOAFrac > this->ZoneMaxOAFraction) {
    4236            0 :                             if (this->ZoneMaxOAFraction > 0.0) {
    4237            0 :                                 curZoneSysEnergyDemand.SupplyAirAdjustFactor = ZoneOAFrac / this->ZoneMaxOAFraction;
    4238              :                             } else {
    4239            0 :                                 curZoneSysEnergyDemand.SupplyAirAdjustFactor = 1.0;
    4240              :                             }
    4241              : 
    4242              :                             // cap zone OA fraction at the maximum specified
    4243            0 :                             ZoneOAFrac = this->ZoneMaxOAFraction;
    4244              :                         }
    4245              :                     }
    4246              : 
    4247              :                     // Zone air secondary recirculation fraction
    4248        31745 :                     Er = thisMechVentZone.ZoneSecondaryRecirculation;
    4249        31745 :                     if (Er > 0.0) {
    4250              :                         // multi-path ventilation system using VRP
    4251            0 :                         Fa = Ep + (1.0 - Ep) * Er;
    4252            0 :                         Fb = Ep;
    4253            0 :                         Fc = 1.0 - (1.0 - ZoneEz) * (1.0 - Er) * (1.0 - Ep);
    4254              : 
    4255              :                         // Calc zone ventilation efficiency
    4256            0 :                         if (Fa > 0.0) {
    4257            0 :                             Evz = 1.0 + Xs * Fb / Fa - ZoneOAFrac * Ep * Fc / Fa;
    4258              :                         } else {
    4259            0 :                             Evz = 1.0;
    4260              :                         }
    4261              :                     } else {
    4262              :                         // single-path ventilation system
    4263        31745 :                         Evz = 1.0 + Xs - ZoneOAFrac;
    4264              :                     }
    4265              : 
    4266              :                     // calc system ventilation efficiency = Minimum of zone ventilation efficiency
    4267        31745 :                     if (Evz < 0.0) {
    4268            0 :                         Evz = 0.0;
    4269              :                     }
    4270        31745 :                     if (Evz < SysEv) {
    4271        26882 :                         SysEv = Evz;
    4272              :                     }
    4273              : 
    4274              :                 } // zone loop
    4275              : 
    4276              :                 // Calc the system supply OA flow rate counting the system ventilation efficiency
    4277        15874 :                 if (SysEv <= 0.0) {
    4278            0 :                     SysEv = 1.0;
    4279              :                 }
    4280              : 
    4281              :                 // Calc system outdoor air requirement
    4282        15874 :                 if (this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlSchOcc ||
    4283        15874 :                     this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOcc ||
    4284        15868 :                     this->SystemOAMethod == DataSizing::SysOAMethod::ProportionalControlDesOARate) {
    4285            8 :                     SysOA = SysOA / SysEv;
    4286        15866 :                 } else if (this->SystemOAMethod == DataSizing::SysOAMethod::VRPL && this->SysDesOA > 0.0) {
    4287              :                     // Limit system OA to design OA minimum flow rate, as per ASHRAE Guideline 36-2018 Section 5.16.3.1
    4288              :                     // If no system sizing run is done (i.e. no Sizing:System) the design outdoor air flow rate is not known
    4289            1 :                     SysOA = min(SysOAuc / SysEv, this->SysDesOA);
    4290              :                 } else {
    4291        15865 :                     SysOA = SysOAuc / SysEv;
    4292              :                 }
    4293              :             }
    4294              : 
    4295              :             // Finally calc the system supply OA mass flow rate
    4296        17975 :             MechVentOAMassFlow = SysOA * state.dataEnvrn->StdRhoAir;
    4297              :         }
    4298              :     }
    4299        17979 :     return MechVentOAMassFlow;
    4300              : }
    4301              : 
    4302        70847 : void OAControllerProps::CalcOAEconomizer(EnergyPlusData &state,
    4303              :                                          int const AirLoopNum,
    4304              :                                          Real64 const OutAirMinFrac,
    4305              :                                          Real64 &OASignal,
    4306              :                                          bool &HighHumidityOperationFlag,
    4307              :                                          bool const FirstHVACIteration)
    4308              : {
    4309        70847 :     int constexpr MaxIte(500);             // Maximum number of iterations
    4310        70847 :     Real64 constexpr Acc(0.0001);          // Accuracy of result
    4311              :     bool AirLoopEconoLockout;              // Economizer lockout flag
    4312              :     bool AirLoopNightVent;                 // Night Ventilation flag for air loop
    4313              :     bool EconomizerOperationFlag;          // TRUE if OA economizer is active
    4314              :     Real64 EconomizerAirFlowScheduleValue; // value of economizer operation schedule (push-button type control schedule)
    4315              :     Real64 MaximumOAFracBySetPoint;        // The maximum OA fraction due to freezing cooling coil check
    4316              :     Real64 OutAirSignal;                   // Used to set OA mass flow rate
    4317              :     Real64 minOAFrac;
    4318              : 
    4319        70847 :     if (AirLoopNum > 0) {
    4320              :         // Check lockout with heating for any airloop - will lockout economizer even on airloops without a unitary system
    4321        70847 :         if (this->Lockout == LockoutType::LockoutWithHeatingPossible) {
    4322              :             // For all system types (even ones that don't set AirLoopEconoLockout) lock out economizer if unfavorable for heating
    4323            2 :             if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CheckHeatRecoveryBypassStatus &&
    4324            1 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysComponentsSimulated) {
    4325              : 
    4326            1 :                 if (this->MixedAirTempAtMinOAFlow <= state.dataLoopNodes->Node(this->MixNode).TempSetPoint) {
    4327            1 :                     state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked = true;
    4328              :                     // this->OAMassFlow = AirLoopFlow( AirLoopNum ).MinOutAir;
    4329              :                     // AirLoopFlow( AirLoopNum ).OAFrac = this->OAMassFlow / this->MixMassFlow;
    4330            1 :                     state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoLockout = true;
    4331              :                 } else {
    4332            0 :                     state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked = false;
    4333            0 :                     this->HRHeatingCoilActive = 0;
    4334              :                 }
    4335            1 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).CheckHeatRecoveryBypassStatus = false;
    4336              :             }
    4337              :         }
    4338              :     }
    4339              : 
    4340        70847 :     if (AirLoopNum > 0) {
    4341        70847 :         AirLoopEconoLockout = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoLockout;
    4342        70847 :         AirLoopNightVent = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).NightVent;
    4343              :     } else {
    4344            0 :         AirLoopEconoLockout = false;
    4345            0 :         AirLoopNightVent = false;
    4346              :     }
    4347              : 
    4348              :     // Define an outside air signal
    4349        70847 :     if (this->MixedAirSPMNum > 0) {
    4350            0 :         this->CoolCoilFreezeCheck = SetPointManager::GetCoilFreezingCheckFlag(state, this->MixedAirSPMNum);
    4351              :     } else {
    4352        70847 :         this->CoolCoilFreezeCheck = false;
    4353              :     }
    4354              : 
    4355        70847 :     if (std::abs(this->RetTemp - this->InletTemp) > HVAC::SmallTempDiff) {
    4356        70836 :         OutAirSignal = (this->RetTemp - this->MixSetTemp) / (this->RetTemp - this->InletTemp);
    4357        70836 :         if (this->CoolCoilFreezeCheck) {
    4358            0 :             this->MaxOAFracBySetPoint = 0.0;
    4359            0 :             MaximumOAFracBySetPoint = OutAirSignal;
    4360              :         }
    4361              :     } else {
    4362           11 :         if (this->RetTemp - this->MixSetTemp < 0.0) {
    4363            0 :             if (this->RetTemp - this->InletTemp >= 0.0) {
    4364            0 :                 OutAirSignal = -1.0;
    4365              :             } else {
    4366            0 :                 OutAirSignal = 1.0;
    4367              :             }
    4368              :         } else {
    4369           11 :             if (this->RetTemp - this->InletTemp >= 0.0) {
    4370           11 :                 OutAirSignal = 1.0;
    4371              :             } else {
    4372            0 :                 OutAirSignal = -1.0;
    4373              :             }
    4374              :         }
    4375              :     }
    4376        70847 :     OutAirSignal = min(max(OutAirSignal, OutAirMinFrac), 1.0);
    4377              : 
    4378              :     // If no economizer, set to minimum and disable economizer and high humidity control
    4379        70847 :     if (this->Econo == EconoOp::NoEconomizer) {
    4380        43430 :         OutAirSignal = OutAirMinFrac;
    4381        43430 :         EconomizerOperationFlag = false;
    4382        43430 :         EconomizerAirFlowScheduleValue = 0.0;
    4383        43430 :         HighHumidityOperationFlag = false;
    4384        27417 :     } else if (this->MaxOA < HVAC::SmallAirVolFlow) {
    4385            0 :         OutAirSignal = OutAirMinFrac;
    4386            0 :         EconomizerOperationFlag = false;
    4387            0 :         EconomizerAirFlowScheduleValue = 0.0;
    4388            0 :         HighHumidityOperationFlag = false;
    4389        27417 :     } else if (AirLoopEconoLockout) {
    4390            1 :         OutAirSignal = OutAirMinFrac;
    4391            1 :         EconomizerOperationFlag = false;
    4392            1 :         EconomizerAirFlowScheduleValue = 0.0;
    4393            1 :         HighHumidityOperationFlag = false;
    4394              :     } else {
    4395              :         // Changed by Amit for new implementation
    4396              :         // Otherwise do the limit checks
    4397        27416 :         EconomizerOperationFlag = true;
    4398              :         // Outside air temp greater than mix air setpoint
    4399        27416 :         if (this->InletTemp > this->MixSetTemp) {
    4400        20272 :             OutAirSignal = 1.0;
    4401              :         }
    4402              :         // Return air temp limit
    4403        27416 :         if (this->Econo == EconoOp::DifferentialDryBulb) {
    4404         3487 :             if (this->InletTemp > this->RetTemp) {
    4405           44 :                 OutAirSignal = OutAirMinFrac;
    4406           44 :                 EconomizerOperationFlag = false;
    4407              :             }
    4408         3487 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4409              :         }
    4410              :         // Return air enthalpy limit
    4411        27416 :         if (this->Econo == EconoOp::DifferentialEnthalpy) {
    4412        15688 :             if (this->InletEnth > this->RetEnth) {
    4413        13337 :                 OutAirSignal = OutAirMinFrac;
    4414        13337 :                 EconomizerOperationFlag = false;
    4415              :             }
    4416        15688 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4417              :         }
    4418              :         // Outside air temperature limit
    4419        27416 :         if (this->Econo == EconoOp::FixedDryBulb) {
    4420         8241 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4421              :         }
    4422              :         // Fixed Enthalpy limit
    4423        27416 :         if (this->Econo == EconoOp::FixedEnthalpy) {
    4424            0 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4425              :         }
    4426              :         // FIXED DEW POINT AND DRY BULB TEMPERATURE STRATEGY
    4427        27416 :         if (this->Econo == EconoOp::FixedDewPointAndDryBulb) {
    4428            0 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4429              :         }
    4430              :         // ELECRONIC ENTHALPY, HUMIDITY RATIO CURVE
    4431        27416 :         if (this->Econo == EconoOp::ElectronicEnthalpy) {
    4432            0 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4433              :         }
    4434              :         // Differential dry bulb and enthalpy strategy
    4435        27416 :         if (this->Econo == EconoOp::DifferentialDryBulbAndEnthalpy) {
    4436            0 :             if (this->InletTemp > this->RetTemp) {
    4437            0 :                 OutAirSignal = OutAirMinFrac;
    4438            0 :                 EconomizerOperationFlag = false;
    4439              :             }
    4440            0 :             if (this->InletEnth > this->RetEnth) {
    4441            0 :                 OutAirSignal = OutAirMinFrac;
    4442            0 :                 EconomizerOperationFlag = false;
    4443              :             }
    4444            0 :             this->Checksetpoints(state, OutAirMinFrac, OutAirSignal, EconomizerOperationFlag);
    4445              :         }
    4446              : 
    4447        27416 :         if (this->TempLowLim != HVAC::BlankNumeric && this->OATemp < this->TempLowLim) {
    4448         5748 :             OutAirSignal = OutAirMinFrac;
    4449         5748 :             EconomizerOperationFlag = false;
    4450              :         }
    4451              :         // Increase air flow for humidity control
    4452              :         // (HumidistatZoneNum is greater than 0 IF High Humidity Control Flag = YES, checked in GetInput)
    4453        27416 :         if (this->HumidistatZoneNum > 0) {
    4454              :             //   IF humidistat senses a moisture load check to see if modifying air flow is appropriate, otherwise disable modified air flow
    4455            4 :             if (state.dataZoneEnergyDemand->ZoneSysMoistureDemand(this->HumidistatZoneNum).TotalOutputRequired < 0.0) {
    4456              :                 //     IF OAController is not allowed to modify air flow during high outdoor humrat condition, then disable modified air flow
    4457              :                 //     if indoor humrat is less than or equal to outdoor humrat
    4458            8 :                 if (!this->ModifyDuringHighOAMoisture &&
    4459            4 :                     (state.dataLoopNodes->Node(this->NodeNumofHumidistatZone).HumRat - this->OAHumRat) <= HVAC::SmallHumRatDiff) {
    4460            3 :                     HighHumidityOperationFlag = false;
    4461              :                 } else {
    4462            1 :                     HighHumidityOperationFlag = true;
    4463              :                 }
    4464              :             } else {
    4465            0 :                 HighHumidityOperationFlag = false;
    4466              :             }
    4467              :         } else {
    4468        27412 :             HighHumidityOperationFlag = false;
    4469              :         }
    4470              : 
    4471              :         // Check time of day economizer schedule, enable economizer if schedule value > 0
    4472        27416 :         EconomizerAirFlowScheduleValue = 0.0;
    4473        27416 :         if (this->economizerOASched != nullptr) {
    4474            0 :             EconomizerAirFlowScheduleValue = this->economizerOASched->getCurrentVal();
    4475            0 :             if (EconomizerAirFlowScheduleValue > 0.0) {
    4476            0 :                 EconomizerOperationFlag = true;
    4477            0 :                 OutAirSignal = 1.0;
    4478              :             }
    4479              :         }
    4480              :     }
    4481              : 
    4482              :     // OutAirSignal will not give exactly the correct mixed air temperature (equal to the setpoint) since
    4483              :     // it was calculated using the approximate method of sensible energy balance. Now we have to get the
    4484              :     // accurate result using a full mass, enthalpy and moisture balance and iteration.
    4485        70847 :     if (OutAirSignal > OutAirMinFrac && OutAirSignal < 1.0 && this->MixMassFlow > HVAC::VerySmallMassFlow &&
    4486         1382 :         this->ControllerType == MixedAirControllerType::ControllerOutsideAir && !AirLoopNightVent) {
    4487              :         int SolFla; // Flag of solver
    4488              : 
    4489         1382 :         if (AirLoopNum > 0) {
    4490              : 
    4491         1382 :             if (state.dataAirLoop->OutsideAirSys(state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum).NumComponents == 1) {
    4492              :                 // no need to simulate OA System if only a mixer is used in the OutsideAirSystem
    4493              : 
    4494         4131 :                 auto f = [&state, this](Real64 const OASignal) {
    4495         4131 :                     Real64 const OAMassFlowRate = OASignal * this->MixMassFlow;
    4496         4131 :                     Real64 const RecircMassFlowRate = max(this->MixMassFlow - OAMassFlowRate, 0.0);
    4497         4131 :                     Real64 const RecircEnth = state.dataLoopNodes->Node(this->RetNode).Enthalpy;
    4498         4131 :                     Real64 const RecircHumRat = state.dataLoopNodes->Node(this->RetNode).HumRat;
    4499              :                     Real64 const MixEnth =
    4500         4131 :                         (RecircMassFlowRate * RecircEnth + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).Enthalpy) / this->MixMassFlow;
    4501              :                     Real64 const MixHumRat =
    4502         4131 :                         (RecircMassFlowRate * RecircHumRat + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).HumRat) / this->MixMassFlow;
    4503         4131 :                     Real64 const MixTemp = Psychrometrics::PsyTdbFnHW(MixEnth, MixHumRat);
    4504         4131 :                     return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - MixTemp;
    4505         1377 :                 };
    4506              : 
    4507         1377 :                 General::SolveRoot(state, Acc, MaxIte, SolFla, OASignal, f, OutAirMinFrac, 1.0);
    4508         1377 :                 if (SolFla < 0) {
    4509            0 :                     OASignal = OutAirSignal;
    4510              :                 }
    4511              : 
    4512              :             } else {
    4513              : 
    4514              :                 // simulate OA System if equipment exists other than the mixer (e.g., heating/cooling coil, HX, etc.)
    4515              : 
    4516              :                 // 1 - check min OA flow result
    4517            5 :                 if (this->FixedMin) {
    4518            4 :                     state.dataLoopNodes->Node(this->OANode).MassFlowRate =
    4519            4 :                         min(max(this->ExhMassFlow, OutAirMinFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply),
    4520            4 :                             state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
    4521            4 :                     state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
    4522            4 :                         max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
    4523              :                     // save actual OA flow frac for use as min value for RegulaFalsi call
    4524            4 :                     minOAFrac = max(OutAirMinFrac, state.dataLoopNodes->Node(this->OANode).MassFlowRate / this->MixMassFlow);
    4525              :                 } else {
    4526            1 :                     state.dataLoopNodes->Node(this->OANode).MassFlowRate =
    4527            1 :                         max(this->ExhMassFlow, OutAirMinFrac * state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
    4528            1 :                     state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
    4529            1 :                         max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
    4530              :                     // save actual OA flow frac for use as min value for RegulaFalsi call
    4531            1 :                     minOAFrac = max(OutAirMinFrac, state.dataLoopNodes->Node(this->OANode).MassFlowRate / this->MixMassFlow);
    4532              :                 }
    4533            5 :                 SimOASysComponents(state, state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum, FirstHVACIteration, AirLoopNum);
    4534            5 :                 Real64 lowFlowResiduum = state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
    4535              : 
    4536              :                 // 2 - check max OA flow result
    4537            5 :                 state.dataLoopNodes->Node(this->OANode).MassFlowRate = max(this->ExhMassFlow, state.dataLoopNodes->Node(this->MixNode).MassFlowRate);
    4538            5 :                 state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
    4539            5 :                     max(state.dataLoopNodes->Node(this->OANode).MassFlowRate - this->ExhMassFlow, 0.0);
    4540            5 :                 SimOASysComponents(state, state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum, FirstHVACIteration, AirLoopNum);
    4541            5 :                 Real64 highFlowResiduum = state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
    4542              : 
    4543              :                 // 3 - test to ensure RegulaFalsi can find an answer
    4544            5 :                 if ((sign(lowFlowResiduum) == sign(highFlowResiduum))) {
    4545            4 :                     OASignal = OutAirSignal;
    4546              :                 } else {
    4547              :                     // 4 - find result
    4548              : 
    4549            4 :                     auto f = [&state, this, FirstHVACIteration, AirLoopNum](Real64 const OASignal) {
    4550            4 :                         Real64 const MixMassFlowRate = this->MixMassFlow;
    4551            4 :                         int const OASysNum = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OASysNum;
    4552            4 :                         Real64 localExhMassFlow = state.dataAirLoop->AirLoopControlInfo(AirLoopNum).ZoneExhMassFlow;
    4553            4 :                         Real64 const OAMassFlowRate = max(localExhMassFlow, OASignal * MixMassFlowRate);
    4554            4 :                         state.dataLoopNodes->Node(this->OANode).MassFlowRate = OAMassFlowRate; // set OA node mass flow rate
    4555            4 :                         state.dataLoopNodes->Node(this->RelNode).MassFlowRate =
    4556            4 :                             max(OAMassFlowRate - localExhMassFlow, 0.0); // set relief node mass flow rate to maintain mixer continuity calcs
    4557            4 :                         SimOASysComponents(state, OASysNum, FirstHVACIteration, AirLoopNum);
    4558            4 :                         return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - state.dataLoopNodes->Node(this->MixNode).Temp;
    4559            1 :                     };
    4560              : 
    4561            1 :                     General::SolveRoot(state, (Acc / 10.0), MaxIte, SolFla, OASignal, f, minOAFrac, 1.0);
    4562            1 :                     if (SolFla < 0) { // if RegulaFalsi fails to find a solution, returns -1 or -2, set to existing OutAirSignal
    4563            0 :                         OASignal = OutAirSignal;
    4564              :                     }
    4565              :                 }
    4566              :             }
    4567              : 
    4568              :         } else {
    4569              : 
    4570            0 :             auto f = [&state, this](Real64 const OASignal) {
    4571            0 :                 Real64 const MixMassFlowRate = this->MixMassFlow;
    4572            0 :                 Real64 OAMassFlowRate = OASignal * MixMassFlowRate;
    4573            0 :                 Real64 RecircMassFlowRate = max(MixMassFlowRate - OAMassFlowRate, 0.0);
    4574            0 :                 Real64 RecircEnth = state.dataLoopNodes->Node(this->RetNode).Enthalpy;
    4575            0 :                 Real64 RecircHumRat = state.dataLoopNodes->Node(this->RetNode).HumRat;
    4576              :                 Real64 MixEnth =
    4577            0 :                     (RecircMassFlowRate * RecircEnth + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).Enthalpy) / MixMassFlowRate;
    4578              :                 Real64 MixHumRat =
    4579            0 :                     (RecircMassFlowRate * RecircHumRat + OAMassFlowRate * state.dataLoopNodes->Node(this->OANode).HumRat) / MixMassFlowRate;
    4580            0 :                 Real64 MixTemp = Psychrometrics::PsyTdbFnHW(MixEnth, MixHumRat);
    4581            0 :                 return state.dataLoopNodes->Node(this->MixNode).TempSetPoint - MixTemp;
    4582            0 :             };
    4583              : 
    4584            0 :             General::SolveRoot(state, Acc, MaxIte, SolFla, OASignal, f, OutAirMinFrac, 1.0);
    4585            0 :             if (SolFla < 0) {
    4586            0 :                 OASignal = OutAirSignal;
    4587              :             }
    4588              :         }
    4589              : 
    4590         1382 :     } else {
    4591        69465 :         OASignal = OutAirSignal;
    4592              :     }
    4593              : 
    4594              :     // Economizer choice "Bypass" forces minimum OA except when high humidity air flow is active based on indoor RH
    4595        70847 :     if (this->EconBypass && EconomizerAirFlowScheduleValue == 0.0) {
    4596        31549 :         OASignal = OutAirMinFrac;
    4597              :     }
    4598              : 
    4599              :     // Set outdoor air signal based on OA flow ratio if high humidity air flow is enabled
    4600        70847 :     if (HighHumidityOperationFlag) {
    4601            1 :         if (this->MixMassFlow > 0.0) {
    4602              :             //   calculate the actual ratio of outside air to mixed air so the magnitude of OA during high humidity control is correct
    4603            1 :             OASignal = max(OutAirMinFrac, (this->HighRHOAFlowRatio * this->MaxOAMassFlowRate / this->MixMassFlow));
    4604            1 :             this->OALimitingFactor = OALimitFactor::HighHum;
    4605              :         }
    4606              :     }
    4607              : 
    4608        70847 :     if (this->CoolCoilFreezeCheck) {
    4609            0 :         MaximumOAFracBySetPoint = min(max(MaximumOAFracBySetPoint, 0.0), 1.0);
    4610            0 :         this->MaxOAFracBySetPoint = MaximumOAFracBySetPoint;
    4611              : 
    4612              :         // This should not be messing with OutAirMinFrac, freeze protection should only limit economizer operation
    4613              :         // if (MaximumOAFracBySetPoint < OutAirMinFrac) {
    4614              :         // OutAirMinFrac = MaximumOAFracBySetPoint;
    4615              :         //    if (AirLoopNum > 0) AirLoopFlow(AirLoopNum).MinOutAir = OutAirMinFrac * this->MixMassFlow;
    4616              :         //}
    4617            0 :         if (MaximumOAFracBySetPoint < OASignal) {
    4618            0 :             OASignal = MaximumOAFracBySetPoint;
    4619            0 :             this->OALimitingFactor = OALimitFactor::Limits;
    4620              :         }
    4621            0 :         if (OutAirMinFrac > OASignal) {
    4622            0 :             OASignal = OutAirMinFrac;
    4623            0 :             this->OALimitingFactor = OALimitFactor::Limits;
    4624              :         }
    4625              :     }
    4626              : 
    4627        70847 :     if (AirLoopNum > 0) {
    4628              : 
    4629              :         // Set the air loop economizer and high humidity control flags.
    4630        70847 :         state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconoActive = EconomizerOperationFlag;
    4631        70847 :         state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HighHumCtrlActive = HighHumidityOperationFlag;
    4632        70847 :         if (state.dataAirLoop->AirLoopControlInfo(AirLoopNum).EconomizerFlowLocked) {
    4633            1 :             this->OAMassFlow = state.dataAirLoop->AirLoopFlow(AirLoopNum).MinOutAir;
    4634            1 :             state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFrac = this->OAMassFlow / this->MixMassFlow;
    4635            1 :             state.dataAirLoop->AirLoopFlow(AirLoopNum).OAFlow = this->OAMassFlow;
    4636              :         }
    4637              : 
    4638              :         // Check heat exchanger bypass control
    4639        70847 :         state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = false;
    4640        70847 :         this->HeatRecoveryBypassStatus = 0;
    4641        70847 :         if (EconomizerOperationFlag) {
    4642         1411 :             if (this->HeatRecoveryBypassControlType == HVAC::BypassWhenWithinEconomizerLimits) {
    4643         1402 :                 state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = true;
    4644         1402 :                 this->HeatRecoveryBypassStatus = 1;
    4645            9 :             } else if (this->HeatRecoveryBypassControlType == HVAC::BypassWhenOAFlowGreaterThanMinimum) {
    4646            9 :                 Real64 OAMassFlowMin = OutAirMinFrac * state.dataAirLoop->AirLoopFlow(AirLoopNum).DesSupply;
    4647            9 :                 Real64 OAMassFlowActual = OASignal * this->MixMassFlow;
    4648            9 :                 Real64 reasonablySmallMassFlow = 1e-6;
    4649            9 :                 if (OAMassFlowActual > (OAMassFlowMin + reasonablySmallMassFlow)) {
    4650            6 :                     state.dataAirLoop->AirLoopControlInfo(AirLoopNum).HeatRecoveryBypass = true;
    4651            6 :                     this->HeatRecoveryBypassStatus = 1;
    4652              :                 }
    4653              :             }
    4654              :         }
    4655              :     }
    4656              : 
    4657              :     // Set economizer report variable and status flag
    4658        70847 :     if (this->Econo == EconoOp::NoEconomizer) {
    4659              :         // No economizer
    4660        43430 :         this->EconomizerStatus = 0;
    4661        43430 :         this->EconoActive = false;
    4662              :     } else {
    4663              :         // With economizer.
    4664        27417 :         if (EconomizerOperationFlag) {
    4665              :             // Economizer is enabled
    4666         1411 :             this->EconomizerStatus = 1;
    4667         1411 :             this->EconoActive = true;
    4668         1411 :             if ((OASignal > OutAirMinFrac) && !HighHumidityOperationFlag) {
    4669         1382 :                 this->OALimitingFactor = OALimitFactor::Economizer;
    4670              :             }
    4671              :         } else {
    4672              :             // Economizer is disabled
    4673        26006 :             this->EconomizerStatus = 0;
    4674        26006 :             this->EconoActive = false;
    4675              :         }
    4676              :     }
    4677              : 
    4678              :     // Night ventilation control overrides economizer and high humidity control.
    4679        70847 :     if (AirLoopNightVent) {
    4680            0 :         OASignal = 1.0;
    4681            0 :         this->OALimitingFactor = OALimitFactor::NightVent;
    4682              :     }
    4683              : 
    4684              :     // Set high humidity control report variable and status flag
    4685        70847 :     if (HighHumidityOperationFlag) {
    4686            1 :         this->HighHumCtrlStatus = 1;
    4687            1 :         this->HighHumCtrlActive = true;
    4688              :     } else {
    4689        70846 :         this->HighHumCtrlStatus = 0;
    4690        70846 :         this->HighHumCtrlActive = false;
    4691              :     }
    4692        70847 : }
    4693              : 
    4694       383145 : void OAMixerProps::CalcOAMixer(EnergyPlusData &state)
    4695              : {
    4696              : 
    4697              :     // SUBROUTINE INFORMATION:
    4698              :     //       AUTHOR         Fred Buhl
    4699              :     //       DATE WRITTEN   Oct 1998
    4700              : 
    4701              :     // PURPOSE OF THIS SUBROUTINE
    4702              :     // Calculate the mixed air flow and conditions
    4703              : 
    4704              :     // Define a recirculation mass flow rate
    4705       383145 :     Real64 RecircMassFlowRate = this->RetMassFlowRate - this->RelMassFlowRate;
    4706              :     // In certain low flow conditions the return air mass flow rate can be below the outside air value established
    4707              :     //  by the user.  This check will ensure that this condition does not result in non-physical air properties.
    4708       383145 :     if (RecircMassFlowRate < 0.0) {
    4709           36 :         RecircMassFlowRate = 0.0;
    4710           36 :         this->RelMassFlowRate = this->RetMassFlowRate;
    4711              :     }
    4712              : 
    4713              :     // Pass through the return air conditions to the relief air stream.  The return air is "split" to
    4714              :     // the relief air and the recirculation air.
    4715       383145 :     this->RelTemp = this->RetTemp;
    4716       383145 :     this->RelHumRat = this->RetHumRat;
    4717       383145 :     this->RelEnthalpy = this->RetEnthalpy;
    4718       383145 :     this->RelPressure = this->RetPressure;
    4719       383145 :     Real64 RecircPressure = this->RetPressure;
    4720       383145 :     Real64 RecircEnthalpy = this->RetEnthalpy;
    4721       383145 :     Real64 RecircHumRat = this->RetHumRat;
    4722              :     // The recirculation air and the outside air are mixed to form the mixed air stream
    4723       383145 :     this->MixMassFlowRate = this->OAMassFlowRate + RecircMassFlowRate;
    4724              :     // Check for zero flow
    4725       383145 :     if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
    4726        57733 :         this->MixEnthalpy = this->RetEnthalpy;
    4727        57733 :         this->MixHumRat = this->RetHumRat;
    4728        57733 :         this->MixPressure = this->RetPressure;
    4729        57733 :         this->MixTemp = this->RetTemp;
    4730        57733 :         return;
    4731              :     }
    4732              : 
    4733       325412 :     this->MixEnthalpy = (RecircMassFlowRate * RecircEnthalpy + this->OAMassFlowRate * this->OAEnthalpy) / this->MixMassFlowRate;
    4734       325412 :     this->MixHumRat = (RecircMassFlowRate * RecircHumRat + this->OAMassFlowRate * this->OAHumRat) / this->MixMassFlowRate;
    4735       325412 :     this->MixPressure = (RecircMassFlowRate * RecircPressure + this->OAMassFlowRate * this->OAPressure) / this->MixMassFlowRate;
    4736              :     // Mixed air temperature is calculated from the mixed air enthalpy and humidity ratio.
    4737       325412 :     this->MixTemp = Psychrometrics::PsyTdbFnHW(this->MixEnthalpy, this->MixHumRat);
    4738              : 
    4739              :     // Check for saturation temperature > dry-bulb temperature and modify temperature at constant enthalpy
    4740       325412 :     Real64 T_sat = Psychrometrics::PsyTsatFnHPb(state, this->MixEnthalpy, this->MixPressure);
    4741       325412 :     if (this->MixTemp < T_sat) {
    4742        13241 :         this->MixTemp = T_sat;
    4743        13241 :         this->MixHumRat = Psychrometrics::PsyWFnTdbH(state, T_sat, this->MixEnthalpy);
    4744              :     }
    4745              : }
    4746              : 
    4747              : // End of Calculation/Simulation Section of the Module
    4748              : //******************************************************************************
    4749              : 
    4750              : // Beginning Sizing Section of the Module
    4751              : //******************************************************************************
    4752              : 
    4753           29 : void OAControllerProps::SizeOAController(EnergyPlusData &state)
    4754              : {
    4755              : 
    4756              :     // SUBROUTINE INFORMATION:
    4757              :     //       AUTHOR         Fred Buhl
    4758              :     //       DATE WRITTEN   September 2001
    4759              : 
    4760              :     // PURPOSE OF THIS SUBROUTINE:
    4761              :     // This subroutine is for sizing OAController Components for which flow rates have not been
    4762              :     // specified in the input.
    4763              : 
    4764              :     // METHODOLOGY EMPLOYED:
    4765              :     // Obtains flow rates from the zone or system sizing arrays.
    4766              : 
    4767              :     // SUBROUTINE PARAMETER DEFINITIONS:
    4768              :     static std::string_view const &CurrentModuleObject(CurrentModuleObjects[static_cast<int>(CMO::OAController)]);
    4769              : 
    4770              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4771           29 :     bool ErrorsFound = false;
    4772           29 :     if (this->MaxOA == AutoSize) {
    4773              : 
    4774           19 :         if (state.dataSize->CurSysNum > 0) {
    4775              : 
    4776           18 :             switch (this->ControllerType) {
    4777           18 :             case MixedAirControllerType::ControllerOutsideAir: {
    4778           18 :                 CheckSysSizing(state, CurrentModuleObject, this->Name);
    4779           18 :                 switch (state.dataSize->CurDuctType) {
    4780            0 :                 case HVAC::AirDuctType::Cooling: {
    4781            0 :                     this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesCoolVolFlow;
    4782            0 :                 } break;
    4783            0 :                 case HVAC::AirDuctType::Heating: {
    4784            0 :                     this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesHeatVolFlow;
    4785            0 :                 } break;
    4786           18 :                 case HVAC::AirDuctType::Main:
    4787              :                 case HVAC::AirDuctType::Other:
    4788              :                 default: {
    4789           18 :                     this->MaxOA = state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesMainVolFlow;
    4790           18 :                 } break;
    4791              :                 }
    4792           18 :             } break;
    4793            0 :             case MixedAirControllerType::ControllerStandAloneERV: {
    4794            0 :             } break;
    4795            0 :             default:
    4796            0 :                 break;
    4797              :             }
    4798              : 
    4799            1 :         } else if (state.dataSize->CurZoneEqNum > 0) {
    4800              : 
    4801            0 :             switch (this->ControllerType) {
    4802            0 :             case MixedAirControllerType::ControllerOutsideAir: {
    4803            0 :                 CheckZoneSizing(state, CurrentModuleObject, this->Name);
    4804            0 :                 this->MaxOA = max(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow,
    4805            0 :                                   state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow);
    4806            0 :             } break;
    4807            0 :             case MixedAirControllerType::ControllerStandAloneERV: {
    4808            0 :             } break;
    4809            0 :             default:
    4810            0 :                 break;
    4811              :             }
    4812              :         }
    4813              : 
    4814           19 :         if (this->MaxOA < HVAC::SmallAirVolFlow) {
    4815            1 :             this->MaxOA = 0.0;
    4816              :         }
    4817              : 
    4818           19 :         BaseSizer::reportSizerOutput(state, CurrentModuleObject, this->Name, "Maximum Outdoor Air Flow Rate [m3/s]", this->MaxOA);
    4819              :     }
    4820              : 
    4821           29 :     if (this->MinOA == AutoSize) {
    4822              : 
    4823           10 :         if (state.dataSize->CurSysNum > 0) {
    4824              : 
    4825            9 :             CheckSysSizing(state, CurrentModuleObject, this->Name);
    4826            9 :             if (state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesOutAirVolFlow >= HVAC::SmallAirVolFlow) {
    4827            6 :                 this->MinOA = min(state.dataSize->FinalSysSizing(state.dataSize->CurSysNum).DesOutAirVolFlow, this->MaxOA);
    4828              :             } else {
    4829            3 :                 this->MinOA = 0.0;
    4830              :             }
    4831              :         }
    4832              : 
    4833           10 :         BaseSizer::reportSizerOutput(state, CurrentModuleObject, this->Name, "Minimum Outdoor Air Flow Rate [m3/s]", this->MinOA);
    4834              : 
    4835           10 :         if (this->HumidistatZoneNum > 0 && this->FixedMin) {
    4836            0 :             if (this->MaxOA > 0.0) {
    4837            0 :                 Real64 OAFlowRatio = this->MinOA / this->MaxOA;
    4838            0 :                 if (this->HighRHOAFlowRatio < OAFlowRatio) {
    4839            0 :                     ShowWarningError(state, format("{} \"{}\"", CurrentModuleObject, this->Name));
    4840            0 :                     ShowContinueError(state, "... A fixed minimum outdoor air flow rate and high humidity control have been specified.");
    4841            0 :                     ShowContinueError(state,
    4842              :                                       "... The High Humidity Outdoor Air Flow Ratio is less than the ratio of the outdoor air controllers "
    4843              :                                       "minimum to maximum outside air flow rate.");
    4844            0 :                     ShowContinueError(state, format("... Controller minimum flow rate = {:.4T} m3/s.", this->MinOA));
    4845            0 :                     ShowContinueError(state, format("... Controller maximum flow rate = {:.4T} m3/s.", this->MaxOA));
    4846            0 :                     ShowContinueError(state, format("... Controller minimum to maximum flow ratio = {:.4T}.", OAFlowRatio));
    4847            0 :                     ShowContinueError(state, format("... High humidity control flow ratio = {:.4T}.", this->HighRHOAFlowRatio));
    4848              :                 }
    4849              :             }
    4850              :         }
    4851              :     }
    4852              :     // If there is an outside air system, loop over components in the OA system; pass the design air flow rate
    4853              :     // to the coil components that don't have design air flow as an input.
    4854           29 :     if (state.dataSize->CurOASysNum > 0) {
    4855           56 :         for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).NumComponents; ++CompNum) {
    4856           31 :             std::string const &CompType = state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).ComponentType(CompNum);
    4857           31 :             std::string const &CompName = state.dataAirLoop->OutsideAirSys(state.dataSize->CurOASysNum).ComponentName(CompNum);
    4858           60 :             if (Util::SameString(CompType, "COIL:COOLING:WATER:DETAILEDGEOMETRY") || Util::SameString(CompType, "COIL:HEATING:WATER") ||
    4859           60 :                 Util::SameString(CompType, "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED")) {
    4860            2 :                 std::string CoilName;
    4861            2 :                 std::string CoilType;
    4862              : 
    4863            2 :                 if (Util::SameString(CompType, "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED")) {
    4864            0 :                     CoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, CompType, CompName, ErrorsFound);
    4865            0 :                     CoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, CompType, CompName, ErrorsFound);
    4866              :                 } else {
    4867            2 :                     CoilName = CompName;
    4868            2 :                     CoilType = CompType;
    4869              :                 }
    4870            2 :                 WaterCoils::SetCoilDesFlow(state, CoilType, CoilName, this->MinOA, ErrorsFound);
    4871            2 :             }
    4872              :         } // End of component loop
    4873              :     }
    4874           29 :     if (ErrorsFound) {
    4875            0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
    4876              :     }
    4877           29 : }
    4878              : 
    4879              : // End of Sizing Section of the Module
    4880              : //******************************************************************************
    4881              : 
    4882              : // Beginning Update/Reporting Section of the Module
    4883              : //******************************************************************************
    4884              : 
    4885        78419 : void OAControllerProps::UpdateOAController(EnergyPlusData &state)
    4886              : {
    4887              : 
    4888              :     // SUBROUTINE INFORMATION:
    4889              :     //       AUTHOR         Fred Buhl
    4890              :     //       DATE WRITTEN   Oct 1998
    4891              :     //       MODIFIED       Shirey/Raustad FSEC, June 2003
    4892              : 
    4893              :     // PURPOSE OF THIS SUBROUTINE
    4894              :     // Move the results of CalcOAController to the affected nodes
    4895              : 
    4896              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4897        78419 :     int OutAirNodeNum = this->OANode;
    4898        78419 :     int InletAirNodeNum = this->InletNode;
    4899        78419 :     int RelAirNodeNum = this->RelNode;
    4900        78419 :     int RetAirNodeNum = this->RetNode;
    4901              : 
    4902        78419 :     if (this->ControllerType == MixedAirControllerType::ControllerOutsideAir) {
    4903              :         // The outside air controller sets the outside air flow rate and the relief air flow rate
    4904        78419 :         if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) &&
    4905            0 :             (this->OAMassFlow > this->DemandLimitFlowRate)) {
    4906            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
    4907            0 :             state.dataLoopNodes->Node(InletAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
    4908            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->DemandLimitFlowRate;
    4909              :         } else {
    4910        78419 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->OAMassFlow;
    4911        78419 :             state.dataLoopNodes->Node(InletAirNodeNum).MassFlowRate = this->OAMassFlow;
    4912        78419 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->OAMassFlow;
    4913              :         }
    4914        78419 :         state.dataLoopNodes->Node(RelAirNodeNum).MassFlowRate = this->RelMassFlow;
    4915              :     } else {
    4916              :         // The ERV controller sets the supply and secondary inlet node information for the Stand Alone ERV
    4917              :         // Currently, the Stand Alone ERV only has constant air flows (supply and exhaust), and these are
    4918              :         // already set in HVACStandAloneERV.cc (subroutine init). Therefore, these flow assignments below are
    4919              :         // currently redundant but may be useful in the future as mass flow rates can vary based on the controller signal.
    4920            0 :         if (!state.dataGlobal->WarmupFlag && !state.dataGlobal->DoingSizing && (this->ManageDemand) &&
    4921            0 :             (this->OAMassFlow > this->DemandLimitFlowRate)) {
    4922            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->DemandLimitFlowRate;
    4923            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->DemandLimitFlowRate;
    4924              :         } else {
    4925            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRate = this->OAMassFlow;
    4926            0 :             state.dataLoopNodes->Node(OutAirNodeNum).MassFlowRateMaxAvail = this->OAMassFlow;
    4927              :         }
    4928            0 :         state.dataLoopNodes->Node(RetAirNodeNum).MassFlowRate = state.dataLoopNodes->Node(this->RetNode).MassFlowRate;
    4929            0 :         state.dataLoopNodes->Node(RetAirNodeNum).MassFlowRateMaxAvail = state.dataLoopNodes->Node(this->RetNode).MassFlowRate;
    4930              :     }
    4931        78419 : }
    4932              : 
    4933       383145 : void OAMixerProps::UpdateOAMixer(EnergyPlusData &state) const
    4934              : {
    4935              : 
    4936              :     // SUBROUTINE INFORMATION:
    4937              :     //       AUTHOR         Fred Buhl
    4938              :     //       DATE WRITTEN   Oct 1998
    4939              : 
    4940              :     // PURPOSE OF THIS SUBROUTINE
    4941              :     // Move the results of CalcOAMixer to the affected nodes
    4942              : 
    4943              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4944       383145 :     int MixNode = this->MixNode;
    4945       383145 :     int RelNode = this->RelNode;
    4946       383145 :     int RetNode = this->RetNode;
    4947              :     // Move mixed air data to the mixed air node
    4948       383145 :     state.dataLoopNodes->Node(MixNode).MassFlowRate = this->MixMassFlowRate;
    4949       383145 :     state.dataLoopNodes->Node(MixNode).Temp = this->MixTemp;
    4950       383145 :     state.dataLoopNodes->Node(MixNode).HumRat = this->MixHumRat;
    4951       383145 :     state.dataLoopNodes->Node(MixNode).Enthalpy = this->MixEnthalpy;
    4952       383145 :     state.dataLoopNodes->Node(MixNode).Press = this->MixPressure;
    4953       383145 :     state.dataLoopNodes->Node(MixNode).MassFlowRateMaxAvail = this->MixMassFlowRate;
    4954              :     // Move the relief air data to the relief air node
    4955       383145 :     state.dataLoopNodes->Node(RelNode).MassFlowRate = this->RelMassFlowRate;
    4956       383145 :     state.dataLoopNodes->Node(RelNode).Temp = this->RelTemp;
    4957       383145 :     state.dataLoopNodes->Node(RelNode).HumRat = this->RelHumRat;
    4958       383145 :     state.dataLoopNodes->Node(RelNode).Enthalpy = this->RelEnthalpy;
    4959       383145 :     state.dataLoopNodes->Node(RelNode).Press = this->RelPressure;
    4960       383145 :     state.dataLoopNodes->Node(RelNode).MassFlowRateMaxAvail = this->RelMassFlowRate;
    4961              : 
    4962       383145 :     if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    4963            0 :         state.dataLoopNodes->Node(RelNode).CO2 = state.dataLoopNodes->Node(RetNode).CO2;
    4964            0 :         if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
    4965            0 :             state.dataLoopNodes->Node(MixNode).CO2 = state.dataLoopNodes->Node(RetNode).CO2;
    4966              :         } else {
    4967            0 :             state.dataLoopNodes->Node(MixNode).CO2 =
    4968            0 :                 ((state.dataLoopNodes->Node(RetNode).MassFlowRate - state.dataLoopNodes->Node(RelNode).MassFlowRate) *
    4969            0 :                      state.dataLoopNodes->Node(RetNode).CO2 +
    4970            0 :                  this->OAMassFlowRate * state.dataContaminantBalance->OutdoorCO2) /
    4971            0 :                 this->MixMassFlowRate;
    4972              :         }
    4973              :     }
    4974              : 
    4975       383145 :     if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    4976            0 :         state.dataLoopNodes->Node(RelNode).GenContam = state.dataLoopNodes->Node(RetNode).GenContam;
    4977            0 :         if (this->MixMassFlowRate <= HVAC::VerySmallMassFlow) {
    4978            0 :             state.dataLoopNodes->Node(MixNode).GenContam = state.dataLoopNodes->Node(RetNode).GenContam;
    4979              :         } else {
    4980            0 :             state.dataLoopNodes->Node(MixNode).GenContam =
    4981            0 :                 ((state.dataLoopNodes->Node(RetNode).MassFlowRate - state.dataLoopNodes->Node(RelNode).MassFlowRate) *
    4982            0 :                      state.dataLoopNodes->Node(RetNode).GenContam +
    4983            0 :                  this->OAMassFlowRate * state.dataContaminantBalance->OutdoorGC) /
    4984            0 :                 this->MixMassFlowRate;
    4985              :         }
    4986              :     }
    4987       383145 : }
    4988              : 
    4989              : // End of Sizing Section of the Module
    4990              : //******************************************************************************
    4991              : 
    4992              : // Beginning Utility Section of the Module
    4993              : //******************************************************************************
    4994              : 
    4995           81 : Array1D_int GetOAMixerNodeNumbers(EnergyPlusData &state,
    4996              :                                   std::string const &OAMixerName, // must match OA mixer names for the OA mixer type
    4997              :                                   bool &ErrorsFound               // set to true if problem
    4998              : )
    4999              : {
    5000              : 
    5001              :     // FUNCTION INFORMATION:
    5002              :     //       AUTHOR         Richard Raustad
    5003              :     //       DATE WRITTEN   June 2006
    5004              : 
    5005              :     // PURPOSE OF THIS FUNCTION:
    5006              :     // This function looks up the given OA mixer and returns the node numbers.  If
    5007              :     // incorrect OA mixer name is given, ErrorsFound is returned as true
    5008              :     // as zero.
    5009              : 
    5010              :     // Return value
    5011           81 :     Array1D_int OANodeNumbers(4); // return OA mixer nodes
    5012              : 
    5013              :     // Obtains and Allocates OA mixer related parameters from input file
    5014           81 :     if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
    5015           38 :         GetOAMixerInputs(state);
    5016           38 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5017              :     }
    5018              : 
    5019           81 :     int WhichOAMixer = Util::FindItemInList(OAMixerName, state.dataMixedAir->OAMixer);
    5020           81 :     if (WhichOAMixer != 0) {
    5021           81 :         OANodeNumbers(1) = state.dataMixedAir->OAMixer(WhichOAMixer).InletNode;
    5022           81 :         OANodeNumbers(2) = state.dataMixedAir->OAMixer(WhichOAMixer).RelNode;
    5023           81 :         OANodeNumbers(3) = state.dataMixedAir->OAMixer(WhichOAMixer).RetNode;
    5024           81 :         OANodeNumbers(4) = state.dataMixedAir->OAMixer(WhichOAMixer).MixNode;
    5025              :     }
    5026              : 
    5027           81 :     if (WhichOAMixer == 0) {
    5028            0 :         ShowSevereError(state, format("GetOAMixerNodeNumbers: Could not find OA Mixer = \"{}\"", OAMixerName));
    5029            0 :         ErrorsFound = true;
    5030            0 :         OANodeNumbers = 0;
    5031              :     }
    5032              : 
    5033           81 :     return OANodeNumbers;
    5034            0 : }
    5035              : 
    5036           29 : int GetNumOAMixers(EnergyPlusData &state)
    5037              : {
    5038              : 
    5039              :     // FUNCTION INFORMATION:
    5040              :     //       AUTHOR         Linda Lawrie
    5041              :     //       DATE WRITTEN   October 2006
    5042              : 
    5043              :     // PURPOSE OF THIS FUNCTION:
    5044              :     // After making sure get input is done, the number of OA mixers is returned.
    5045              : 
    5046           29 :     if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
    5047            5 :         GetOAMixerInputs(state);
    5048            5 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5049              :     }
    5050              : 
    5051           29 :     return state.dataMixedAir->NumOAMixers;
    5052              : }
    5053              : 
    5054            2 : int GetNumOAControllers(EnergyPlusData &state)
    5055              : {
    5056              : 
    5057              :     // FUNCTION INFORMATION:
    5058              :     //       AUTHOR         Linda Lawrie
    5059              :     //       DATE WRITTEN   October 2006
    5060              : 
    5061              :     // PURPOSE OF THIS FUNCTION:
    5062              :     // After making sure get input is done, the number of OA Controllers is returned.
    5063              : 
    5064            2 :     if (state.dataMixedAir->AllocateOAControllersFlag) {
    5065              :         // Make sure OAControllers are allocated
    5066            0 :         AllocateOAControllers(state);
    5067              :     }
    5068              : 
    5069            2 :     return state.dataMixedAir->NumOAControllers;
    5070              : }
    5071              : 
    5072            2 : int GetOAMixerReliefNodeNumber(EnergyPlusData &state, int const OAMixerNum) // Which Mixer
    5073              : {
    5074              : 
    5075              :     // FUNCTION INFORMATION:
    5076              :     //       AUTHOR         Linda Lawrie
    5077              :     //       DATE WRITTEN   October 2006
    5078              : 
    5079              :     // PURPOSE OF THIS FUNCTION:
    5080              :     // After making sure get input is done, the relief node number of indicated mixer is returned.
    5081              : 
    5082            2 :     if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
    5083            0 :         GetOAMixerInputs(state);
    5084            0 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5085              :     }
    5086              : 
    5087            2 :     if (OAMixerNum > state.dataMixedAir->NumOAMixers) {
    5088            0 :         ShowFatalError(state,
    5089            0 :                        format("GetOAMixerReliefNodeNumber: Requested Mixer #={}, which is > number of OA Mixers={}",
    5090              :                               OAMixerNum,
    5091            0 :                               state.dataMixedAir->NumOAMixers));
    5092              :     }
    5093              : 
    5094            2 :     return state.dataMixedAir->OAMixer(OAMixerNum).RelNode;
    5095              : }
    5096              : 
    5097           48 : int GetOASysControllerListIndex(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
    5098              : {
    5099              : 
    5100              :     // FUNCTION INFORMATION:
    5101              :     //       AUTHOR         Fred Buhl
    5102              :     //       DATE WRITTEN   April 2007
    5103              : 
    5104              :     // PURPOSE OF THIS FUNCTION:
    5105              :     // After making sure get input is done, the Controller List index of the indicated OA System is returned.
    5106              : 
    5107           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5108            0 :         GetOutsideAirSysInputs(state);
    5109            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5110              :     }
    5111              : 
    5112           48 :     return state.dataAirLoop->OutsideAirSys(OASysNumber).ControllerListNum;
    5113              : }
    5114              : 
    5115           48 : int GetOASysNumSimpControllers(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
    5116              : {
    5117              : 
    5118              :     // FUNCTION INFORMATION:
    5119              :     //       AUTHOR         Fred Buhl
    5120              :     //       DATE WRITTEN   April 2007
    5121              : 
    5122              :     // PURPOSE OF THIS FUNCTION:
    5123              :     // After making sure get input is done, the number of Controller:Simple objects in the OA System is returned.
    5124              : 
    5125           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5126            0 :         GetOutsideAirSysInputs(state);
    5127            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5128              :     }
    5129              : 
    5130           48 :     return state.dataAirLoop->OutsideAirSys(OASysNumber).NumSimpleControllers;
    5131              : }
    5132              : 
    5133           48 : int GetOASysNumHeatingCoils(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
    5134              : {
    5135              : 
    5136              :     // FUNCTION INFORMATION:
    5137              :     //       AUTHOR         Fred Buhl
    5138              :     //       DATE WRITTEN   May 2007
    5139              : 
    5140              :     // PURPOSE OF THIS FUNCTION:
    5141              :     // After making sure get input is done, the number of heating coils in the OA System is returned.
    5142              : 
    5143              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    5144           48 :     bool Sim(false);
    5145           48 :     bool FirstHVACIteration(false);
    5146           48 :     bool OAHeatingCoil(false);
    5147           48 :     bool OACoolingCoil(false);
    5148           48 :     int AirLoopNum(0);
    5149           48 :     bool OAHX(false);
    5150              : 
    5151           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5152            0 :         GetOutsideAirSysInputs(state);
    5153            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5154              :     }
    5155              : 
    5156           48 :     int NumHeatingCoils = 0;
    5157          105 :     for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
    5158           57 :         std::string const &CompType = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(CompNum);
    5159           57 :         std::string const &CompName = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(CompNum);
    5160          228 :         SimOAComponent(state,
    5161              :                        CompType,
    5162              :                        CompName,
    5163           57 :                        state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum(CompNum),
    5164              :                        FirstHVACIteration,
    5165           57 :                        state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentIndex(CompNum),
    5166              :                        AirLoopNum,
    5167              :                        Sim,
    5168              :                        OASysNumber,
    5169              :                        OAHeatingCoil,
    5170              :                        OACoolingCoil,
    5171              :                        OAHX);
    5172           57 :         if (OAHeatingCoil) {
    5173            3 :             ++NumHeatingCoils;
    5174              :         }
    5175              :     }
    5176              : 
    5177           48 :     return NumHeatingCoils;
    5178              : }
    5179              : 
    5180           48 : int GetOASysNumHXs(EnergyPlusData &state, int const OASysNumber)
    5181              : {
    5182              : 
    5183              :     // FUNCTION INFORMATION:
    5184              :     //       AUTHOR         Fred Buhl, Rongpeng Zhang
    5185              :     //       DATE WRITTEN   Oct. 2015
    5186              : 
    5187              :     // PURPOSE OF THIS FUNCTION:
    5188              :     // After making sure get input is done, the number of heat recovery exchangers in the OA System is returned.
    5189              : 
    5190           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5191            0 :         GetOutsideAirSysInputs(state);
    5192            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5193              :     }
    5194              : 
    5195           48 :     int NumHX = 0;
    5196              : 
    5197           48 :     auto const &componentType_Num = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum;
    5198          105 :     for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
    5199           57 :         SimAirServingZones::CompType const componentTypeNum = componentType_Num(CompNum);
    5200           57 :         if (SimAirServingZones::CompType::HeatXchngr == componentTypeNum || SimAirServingZones::CompType::Desiccant == componentTypeNum) {
    5201            3 :             ++NumHX;
    5202              :         }
    5203              :     }
    5204              : 
    5205           48 :     return NumHX;
    5206              : }
    5207              : 
    5208           48 : int GetOASysNumCoolingCoils(EnergyPlusData &state, int const OASysNumber) // OA Sys Number
    5209              : {
    5210              : 
    5211              :     // FUNCTION INFORMATION:
    5212              :     //       AUTHOR         Fred Buhl
    5213              :     //       DATE WRITTEN   May 2007
    5214              : 
    5215              :     // PURPOSE OF THIS FUNCTION:
    5216              :     // After making sure get input is done, the number of cooling coils in the OA System is returned.
    5217              : 
    5218              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    5219           48 :     bool Sim(false);
    5220           48 :     bool FirstHVACIteration(false);
    5221           48 :     bool OAHeatingCoil(false);
    5222           48 :     bool OACoolingCoil(false);
    5223           48 :     int AirLoopNum(0);
    5224           48 :     bool OAHX(false);
    5225              : 
    5226           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5227            0 :         GetOutsideAirSysInputs(state);
    5228            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5229              :     }
    5230              : 
    5231           48 :     int NumCoolingCoils = 0;
    5232          105 :     for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++CompNum) {
    5233           57 :         std::string const &CompType = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(CompNum);
    5234           57 :         std::string const &CompName = state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(CompNum);
    5235          228 :         SimOAComponent(state,
    5236              :                        CompType,
    5237              :                        CompName,
    5238           57 :                        state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentTypeEnum(CompNum),
    5239              :                        FirstHVACIteration,
    5240           57 :                        state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentIndex(CompNum),
    5241              :                        AirLoopNum,
    5242              :                        Sim,
    5243              :                        OASysNumber,
    5244              :                        OAHeatingCoil,
    5245              :                        OACoolingCoil,
    5246              :                        OAHX);
    5247           57 :         if (OACoolingCoil) {
    5248            3 :             ++NumCoolingCoils;
    5249              :         }
    5250              :     }
    5251              : 
    5252           48 :     return NumCoolingCoils;
    5253              : }
    5254              : 
    5255           48 : int GetOASystemNumber(EnergyPlusData &state, std::string const &OASysName) // OA Sys Name
    5256              : {
    5257              : 
    5258              :     // FUNCTION INFORMATION:
    5259              :     //       AUTHOR         Linda Lawrie
    5260              :     //       DATE WRITTEN   October 2006
    5261              : 
    5262              :     // PURPOSE OF THIS FUNCTION:
    5263              :     // After making sure get input is done, the OA System number of indicated OA System is returned.
    5264              : 
    5265           48 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5266           35 :         GetOutsideAirSysInputs(state);
    5267           35 :         state.dataMixedAir->GetOASysInputFlag = false;
    5268              :     }
    5269              : 
    5270           48 :     return Util::FindItemInList(OASysName, state.dataAirLoop->OutsideAirSys);
    5271              : }
    5272              : 
    5273           48 : int FindOAMixerMatchForOASystem(EnergyPlusData &state, int const OASysNumber) // Which OA System
    5274              : {
    5275              : 
    5276              :     // FUNCTION INFORMATION:
    5277              :     //       AUTHOR         Linda Lawrie
    5278              :     //       DATE WRITTEN   October 2006
    5279              : 
    5280              :     // PURPOSE OF THIS FUNCTION:
    5281              :     // After making sure get input is done, the matched mixer number is found.
    5282              :     // Note -- only the first is looked at for an Outside Air System.
    5283              : 
    5284           48 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
    5285           36 :         GetOAMixerInputs(state);
    5286           36 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5287              :     }
    5288              : 
    5289           48 :     int OAMixerNumber = 0;
    5290           48 :     if (OASysNumber > 0 && OASysNumber <= state.dataAirLoop->NumOASystems) {
    5291           57 :         for (int OACompNum = 1; OACompNum <= state.dataAirLoop->OutsideAirSys(OASysNumber).NumComponents; ++OACompNum) {
    5292           57 :             if (Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentType(OACompNum), "OUTDOORAIR:MIXER")) {
    5293              :                 OAMixerNumber =
    5294           48 :                     Util::FindItemInList(state.dataAirLoop->OutsideAirSys(OASysNumber).ComponentName(OACompNum), state.dataMixedAir->OAMixer);
    5295           48 :                 break;
    5296              :             }
    5297              :         }
    5298              :     }
    5299              : 
    5300           48 :     return OAMixerNumber;
    5301              : }
    5302              : 
    5303           20 : int GetOAMixerIndex(EnergyPlusData &state, std::string const &OAMixerName) // Which Mixer
    5304              : {
    5305              : 
    5306              :     // FUNCTION INFORMATION:
    5307              :     //       AUTHOR         Linda Lawrie
    5308              :     //       DATE WRITTEN   December 2010
    5309              : 
    5310              :     // PURPOSE OF THIS FUNCTION:
    5311              :     // After making sure get input is done, the mixer index of indicated mixer is returned.
    5312              : 
    5313           20 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
    5314            6 :         GetOAMixerInputs(state);
    5315            6 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5316              :     }
    5317              : 
    5318           20 :     int OAMixerIndex = Util::FindItem(OAMixerName, state.dataMixedAir->OAMixer);
    5319              : 
    5320           20 :     if (OAMixerIndex == 0) {
    5321            0 :         ShowSevereError(state, format("GetOAMixerIndex: Could not find OutdoorAir:Mixer, Name=\"{}\"", OAMixerName));
    5322              :     }
    5323              : 
    5324           20 :     return OAMixerIndex;
    5325              : }
    5326              : 
    5327           48 : int GetOAMixerInletNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
    5328              : {
    5329              : 
    5330              :     // FUNCTION INFORMATION:
    5331              :     //       AUTHOR         Linda Lawrie
    5332              :     //       DATE WRITTEN   October 2006
    5333              : 
    5334              :     // PURPOSE OF THIS FUNCTION:
    5335              :     // After making sure get input is done, the mixer inlet node number of indicated mixer is returned.
    5336              : 
    5337           48 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
    5338            0 :         GetOAMixerInputs(state);
    5339            0 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5340              :     }
    5341              : 
    5342           48 :     int OAMixerInletNodeNumber = 0;
    5343           48 :     if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
    5344           48 :         OAMixerInletNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).InletNode;
    5345              :     }
    5346              : 
    5347           48 :     return OAMixerInletNodeNumber;
    5348              : }
    5349              : 
    5350         1584 : int GetOAMixerReturnNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
    5351              : {
    5352              : 
    5353              :     // FUNCTION INFORMATION:
    5354              :     //       AUTHOR         Brent Griffith
    5355              :     //       DATE WRITTEN   December 2006
    5356              : 
    5357              :     // PURPOSE OF THIS FUNCTION:
    5358              :     // After making sure get input is done, the mixer return node number of indicated mixer is returned.
    5359              : 
    5360              :     // METHODOLOGY EMPLOYED:
    5361              :     // followed Linda Lawrie's GetOAMixerInletNodeNumber
    5362              : 
    5363         1584 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
    5364            0 :         GetOAMixerInputs(state);
    5365            0 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5366              :     }
    5367              : 
    5368         1584 :     int OAMixerReturnNodeNumber = 0;
    5369         1584 :     if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
    5370         1584 :         OAMixerReturnNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).RetNode;
    5371              :     }
    5372              : 
    5373         1584 :     return OAMixerReturnNodeNumber;
    5374              : }
    5375              : 
    5376         1584 : int GetOAMixerMixedNodeNumber(EnergyPlusData &state, int const OAMixerNumber) // Which Mixer
    5377              : {
    5378              : 
    5379              :     // FUNCTION INFORMATION:
    5380              :     //       AUTHOR         Brent Griffith
    5381              :     //       DATE WRITTEN   December 2006
    5382              : 
    5383              :     // PURPOSE OF THIS FUNCTION:
    5384              :     // After making sure get input is done, the mixer mixed air node number of indicated mixer is returned.
    5385              : 
    5386         1584 :     if (state.dataMixedAir->GetOAMixerInputFlag) {
    5387            0 :         GetOAMixerInputs(state);
    5388            0 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5389              :     }
    5390              : 
    5391         1584 :     int OAMixerMixedNodeNumber = 0;
    5392         1584 :     if (OAMixerNumber > 0 && OAMixerNumber <= state.dataMixedAir->NumOAMixers) {
    5393         1584 :         OAMixerMixedNodeNumber = state.dataMixedAir->OAMixer(OAMixerNumber).MixNode;
    5394              :     }
    5395              : 
    5396         1584 :     return OAMixerMixedNodeNumber;
    5397              : }
    5398              : 
    5399           30 : bool CheckForControllerWaterCoil(EnergyPlusData &state,
    5400              :                                  DataAirLoop::ControllerKind ControllerType, // should be passed in as UPPERCASE
    5401              :                                  std::string const &ControllerName           // should be passed in as UPPERCASE
    5402              : )
    5403              : {
    5404              : 
    5405              :     // FUNCTION INFORMATION:
    5406              :     //       AUTHOR         Linda Lawrie
    5407              :     //       DATE WRITTEN   May 2009
    5408              : 
    5409              :     // PURPOSE OF THIS FUNCTION:
    5410              :     // This routine checks the controller list for existence of the reference coil.
    5411              : 
    5412           30 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5413           11 :         GetOutsideAirSysInputs(state);
    5414           11 :         state.dataMixedAir->GetOASysInputFlag = false;
    5415              :     }
    5416              : 
    5417           30 :     int OnControllerList = false;
    5418              : 
    5419           72 :     for (int Num = 1; Num <= state.dataMixedAir->NumControllerLists; ++Num) {
    5420           64 :         for (int CompNum = 1; CompNum <= state.dataMixedAir->ControllerLists(Num).NumControllers; ++CompNum) {
    5421              : 
    5422           52 :             if (state.dataMixedAir->ControllerLists(Num).ControllerType(CompNum) != ControllerType) {
    5423           16 :                 continue;
    5424              :             }
    5425           36 :             if (!Util::SameString(state.dataMixedAir->ControllerLists(Num).ControllerName(CompNum), ControllerName)) {
    5426            6 :                 continue;
    5427              :             }
    5428           30 :             OnControllerList = true;
    5429           30 :             break;
    5430              :         }
    5431              :     }
    5432              : 
    5433           30 :     return OnControllerList;
    5434              : }
    5435              : 
    5436           73 : void CheckControllerLists(EnergyPlusData &state, bool &ErrFound)
    5437              : {
    5438              : 
    5439              :     // SUBROUTINE INFORMATION:
    5440              :     //       AUTHOR         Linda Lawrie
    5441              :     //       DATE WRITTEN   May 2009
    5442              : 
    5443              :     // PURPOSE OF THIS SUBROUTINE:
    5444              :     // This routine checks for a "dangling" controller list (AirLoopHVAC:ControllerList).
    5445              :     // It must be either found on a AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem.
    5446              : 
    5447              :     // SUBROUTINE PARAMETER DEFINITIONS:
    5448          201 :     static std::string const CurrentModuleObject("AirLoopHVAC:ControllerList");
    5449          201 :     static std::string const AirLoopObject("AirLoopHVAC");
    5450              : 
    5451              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5452              :     int NumAlphas;
    5453              :     int NumNumbers;
    5454              :     int IOStat;
    5455              : 
    5456           73 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5457           51 :         GetOutsideAirSysInputs(state);
    5458           51 :         state.dataMixedAir->GetOASysInputFlag = false;
    5459              :     }
    5460              : 
    5461           73 :     int NumControllers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
    5462           73 :     int NumAirLoop = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, AirLoopObject);
    5463           73 :     std::string_view AirLoopName = "";
    5464              : 
    5465           96 :     for (int Item = 1; Item <= NumControllers; ++Item) {
    5466              : 
    5467           46 :         state.dataInputProcessing->inputProcessor->getObjectItem(
    5468           23 :             state, CurrentModuleObject, Item, state.dataIPShortCut->cAlphaArgs, NumAlphas, state.dataIPShortCut->rNumericArgs, NumNumbers, IOStat);
    5469           23 :         std::string const ControllerListName = state.dataIPShortCut->cAlphaArgs(1);
    5470           23 :         int Count = 0;
    5471              : 
    5472              :         // Check AirLoopHVAC -- brute force, get each AirLoopHVAC
    5473              : 
    5474           46 :         for (int Loop = 1; Loop <= NumAirLoop; ++Loop) {
    5475           46 :             state.dataInputProcessing->inputProcessor->getObjectItem(
    5476           23 :                 state, AirLoopObject, Loop, state.dataIPShortCut->cAlphaArgs, NumAlphas, state.dataIPShortCut->rNumericArgs, NumNumbers, IOStat);
    5477           23 :             if (state.dataIPShortCut->cAlphaArgs(2) != ControllerListName) {
    5478           20 :                 continue;
    5479              :             }
    5480            3 :             if (++Count == 1) {
    5481            3 :                 AirLoopName = state.dataIPShortCut->cAlphaArgs(1);
    5482              :             }
    5483              :         }
    5484              : 
    5485              :         //  Now check AirLoopHVAC and AirLoopHVAC:OutdoorAirSystem
    5486           23 :         int Found = 0;
    5487           23 :         if (state.dataAirLoop->NumOASystems > 0) {
    5488           22 :             Found = Util::FindItemInList(ControllerListName, state.dataAirLoop->OutsideAirSys, &OutsideAirSysProps::ControllerListName);
    5489           22 :             if (Found > 0) {
    5490           20 :                 ++Count;
    5491              :             }
    5492              :         }
    5493              : 
    5494           23 :         if (Count == 0) {
    5495            0 :             ShowSevereError(state,
    5496            0 :                             format("{}=\"{}\" is not referenced on a AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem object.",
    5497              :                                    CurrentModuleObject,
    5498              :                                    ControllerListName));
    5499            0 :             ErrFound = true;
    5500           23 :         } else if (Count > 1) {
    5501            0 :             ShowSevereError(state,
    5502            0 :                             format("{}=\"{}\" has too many references on AirLoopHVAC or AirLoopHVAC:OutdoorAirSystem objects.",
    5503              :                                    CurrentModuleObject,
    5504              :                                    ControllerListName));
    5505            0 :             if (Found > 0) {
    5506            0 :                 ShowContinueError(state, format("...AirLoopHVAC:OutdoorAirSystem=\"{}\".", state.dataAirLoop->OutsideAirSys(Found).Name));
    5507              :             }
    5508            0 :             ShowContinueError(state, format("...also on AirLoopHVAC=\"{}\".", AirLoopName));
    5509            0 :             ErrFound = true;
    5510              :         }
    5511           23 :     }
    5512           73 : }
    5513              : 
    5514            0 : void CheckOAControllerName(
    5515              :     EnergyPlusData &state, std::string &OAControllerName, std::string const &ObjectType, std::string const &FieldName, bool &ErrorsFound)
    5516              : {
    5517              : 
    5518              :     // SUBROUTINE INFORMATION:
    5519              :     //       AUTHOR         Linda Lawrie
    5520              :     //       DATE WRITTEN   October 2006
    5521              : 
    5522              :     // PURPOSE OF THIS SUBROUTINE:
    5523              :     // When OA Controller data is gotten from other routines, must check to make sure
    5524              :     // new name doesn't duplicate.  (Essentially a pass through to call Verify Name)
    5525              :     // Currently, this is only called from HVACStandAlongERV::GetStandaloneERV()
    5526              : 
    5527            0 :     if (state.dataMixedAir->AllocateOAControllersFlag) {
    5528              :         // Make sure OAControllers are allocated
    5529            0 :         AllocateOAControllers(state);
    5530              :     }
    5531              : 
    5532            0 :     GlobalNames::VerifyUniqueInterObjectName(
    5533            0 :         state, state.dataMixedAir->OAControllerUniqueNames, OAControllerName, ObjectType, FieldName, ErrorsFound);
    5534            0 : }
    5535              : 
    5536        27416 : void OAControllerProps::Checksetpoints(EnergyPlusData &state,
    5537              :                                        Real64 const OutAirMinFrac,   // Local variable used to calculate min OA fraction
    5538              :                                        Real64 &OutAirSignal,         // Used to set OA mass flow rate
    5539              :                                        bool &EconomizerOperationFlag // logical used to show economizer status
    5540              : )
    5541              : {
    5542              : 
    5543              :     // SUBROUTINE INFORMATION:
    5544              :     //       AUTHOR         Amit bhansali
    5545              :     //       DATE WRITTEN   August 2008?
    5546              : 
    5547              :     // PURPOSE OF THIS SUBROUTINE:
    5548              :     // This subroutine checks the setpoints of the upper limits of temperatures, limit enthalpy
    5549              :     // Limit dew point, Enthalpy curve
    5550              : 
    5551        27416 :     if (this->TempLim != HVAC::BlankNumeric && this->OATemp > this->TempLim) {
    5552         6869 :         OutAirSignal = OutAirMinFrac;
    5553         6869 :         EconomizerOperationFlag = false;
    5554              :     }
    5555              :     // Outside air enthalpy limit
    5556        27416 :     if (this->EnthLim != HVAC::BlankNumeric && this->OAEnth > this->EnthLim) {
    5557           47 :         OutAirSignal = OutAirMinFrac;
    5558           47 :         EconomizerOperationFlag = false;
    5559              :     }
    5560              : 
    5561        27416 :     if (this->DPTempLim != HVAC::BlankNumeric) {
    5562         2842 :         Real64 OADPTemp = Psychrometrics::PsyTdpFnWPb(state, this->OAHumRat, this->OAPress);
    5563         2842 :         if (OADPTemp > this->DPTempLim) {
    5564            0 :             OutAirSignal = OutAirMinFrac;
    5565            0 :             EconomizerOperationFlag = false;
    5566              :         }
    5567              :     }
    5568              : 
    5569        27416 :     if (this->EnthalpyCurvePtr > 0) {
    5570            0 :         if (this->OAHumRat > Curve::CurveValue(state, this->EnthalpyCurvePtr, this->OATemp)) {
    5571            0 :             OutAirSignal = OutAirMinFrac;
    5572            0 :             EconomizerOperationFlag = false;
    5573              :         }
    5574              :     }
    5575        27416 : }
    5576              : 
    5577           60 : int GetNumOASystems(EnergyPlusData &state)
    5578              : {
    5579              : 
    5580              :     // FUNCTION INFORMATION:
    5581              :     //       AUTHOR         Linda Lawrie
    5582              :     //       DATE WRITTEN   November 2010
    5583              : 
    5584              :     // PURPOSE OF THIS FUNCTION:
    5585              :     // Get Number of OA Systems, After making sure get input is done
    5586              : 
    5587           60 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5588           10 :         GetOutsideAirSysInputs(state);
    5589           10 :         state.dataMixedAir->GetOASysInputFlag = false;
    5590              :     }
    5591              : 
    5592           60 :     return state.dataAirLoop->NumOASystems;
    5593              : }
    5594              : 
    5595           52 : int GetOACompListNumber(EnergyPlusData &state, int const OASysNum) // OA Sys Number
    5596              : {
    5597              : 
    5598              :     // FUNCTION INFORMATION:
    5599              :     //       AUTHOR         Heejin Cho
    5600              :     //       DATE WRITTEN   November 2010
    5601              : 
    5602              :     // PURPOSE OF THIS FUNCTION:
    5603              :     // After making sure get input is done, the OA System number of indicated OA System is returned.
    5604              : 
    5605           52 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5606            0 :         GetOutsideAirSysInputs(state);
    5607            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5608              :     }
    5609              : 
    5610           52 :     return state.dataAirLoop->OutsideAirSys(OASysNum).NumComponents;
    5611              : }
    5612              : 
    5613            4 : std::string GetOACompName(EnergyPlusData &state,
    5614              :                           int const OASysNum, // OA Sys Number
    5615              :                           int const InListNum // In-list Number
    5616              : )
    5617              : {
    5618              : 
    5619              :     // FUNCTION INFORMATION:
    5620              :     //       AUTHOR         Heejin Cho
    5621              :     //       DATE WRITTEN   November 2010
    5622              : 
    5623              :     // PURPOSE OF THIS FUNCTION:
    5624              :     // After making sure get input is done, the number of heating coils in the OA System is returned.
    5625              : 
    5626            4 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5627            0 :         GetOutsideAirSysInputs(state);
    5628            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5629              :     }
    5630              : 
    5631            4 :     return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentName(InListNum);
    5632              : }
    5633              : 
    5634            4 : std::string GetOACompType(EnergyPlusData &state,
    5635              :                           int const OASysNum, // OA Sys Number
    5636              :                           int const InListNum // In-list Number
    5637              : )
    5638              : {
    5639              : 
    5640              :     // FUNCTION INFORMATION:
    5641              :     //       AUTHOR         Heejin Cho
    5642              :     //       DATE WRITTEN   November 2010
    5643              : 
    5644              :     // PURPOSE OF THIS FUNCTION:
    5645              :     // After making sure get input is done, the number of heating coils in the OA System is returned.
    5646              : 
    5647            4 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5648            0 :         GetOutsideAirSysInputs(state);
    5649            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5650              :     }
    5651              : 
    5652            4 :     return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentType(InListNum);
    5653              : }
    5654              : 
    5655           67 : SimAirServingZones::CompType GetOACompTypeNum(EnergyPlusData &state,
    5656              :                                               int const OASysNum, // OA Sys Number
    5657              :                                               int const InListNum // In-list Number
    5658              : )
    5659              : {
    5660              : 
    5661              :     // FUNCTION INFORMATION:
    5662              :     //       AUTHOR         Heejin Cho
    5663              :     //       DATE WRITTEN   November 2010
    5664              : 
    5665              :     // PURPOSE OF THIS FUNCTION:
    5666              :     // After making sure get input is done, the number of heating coils in the OA System is returned.
    5667              : 
    5668           67 :     if (state.dataMixedAir->GetOASysInputFlag) {
    5669            0 :         GetOutsideAirSysInputs(state);
    5670            0 :         state.dataMixedAir->GetOASysInputFlag = false;
    5671              :     }
    5672              : 
    5673           67 :     return state.dataAirLoop->OutsideAirSys(OASysNum).ComponentTypeEnum(InListNum);
    5674              : }
    5675              : 
    5676            5 : int GetOAMixerNumber(EnergyPlusData &state, std::string const &OAMixerName // must match OA mixer names for the OA mixer type
    5677              : )
    5678              : {
    5679              : 
    5680              :     // FUNCTION INFORMATION:
    5681              :     //       AUTHOR         Lixing Gu
    5682              :     //       DATE WRITTEN   Feb. 2018
    5683              : 
    5684              :     // PURPOSE OF THIS FUNCTION:
    5685              :     // This function looks up the given OA mixer and returns the OAMixer number.  If
    5686              :     // incorrect OA mixer name is given, ErrorsFound is returned as true
    5687              : 
    5688              :     // Obtains and Allocates OA mixer related parameters from input file
    5689            5 :     if (state.dataMixedAir->GetOAMixerInputFlag) { // First time subroutine has been entered
    5690            2 :         GetOAMixerInputs(state);
    5691            2 :         state.dataMixedAir->GetOAMixerInputFlag = false;
    5692              :     }
    5693              : 
    5694            5 :     return Util::FindItemInList(OAMixerName, state.dataMixedAir->OAMixer);
    5695              : }
    5696              : // End of Utility Section of the Module
    5697              : //******************************************************************************
    5698              : 
    5699              : } // namespace EnergyPlus::MixedAir
        

Generated by: LCOV version 2.0-1