LCOV - code coverage report
Current view: top level - EnergyPlus - MixedAir.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 64.9 % 2771 1798
Test Date: 2025-05-22 16:09:37 Functions: 87.7 % 57 50

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

Generated by: LCOV version 2.0-1