LCOV - code coverage report
Current view: top level - EnergyPlus - MixedAir.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 73.0 % 2884 2106
Test Date: 2025-07-17 05:04:31 Functions: 94.6 % 56 53

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

Generated by: LCOV version 2.0-1