LCOV - code coverage report
Current view: top level - EnergyPlus - Fans.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1276 1630 78.3 %
Date: 2024-08-23 23:50:59 Functions: 23 24 95.8 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Fmath.hh>
      53             : 
      54             : // EnergyPlus Headers
      55             : #include <AirflowNetwork/Solver.hpp>
      56             : #include <EnergyPlus/AirLoopHVACDOAS.hh>
      57             : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.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/DataEnvironment.hh>
      63             : #include <EnergyPlus/DataHeatBalance.hh>
      64             : #include <EnergyPlus/DataIPShortCuts.hh>
      65             : #include <EnergyPlus/DataLoopNode.hh>
      66             : #include <EnergyPlus/DataPrecisionGlobals.hh>
      67             : #include <EnergyPlus/DataSizing.hh>
      68             : #include <EnergyPlus/DataZoneEquipment.hh>
      69             : #include <EnergyPlus/EMSManager.hh>
      70             : #include <EnergyPlus/Fans.hh>
      71             : #include <EnergyPlus/FaultsManager.hh>
      72             : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
      73             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      74             : #include <EnergyPlus/NodeInputManager.hh>
      75             : #include <EnergyPlus/OutputProcessor.hh>
      76             : #include <EnergyPlus/OutputReportPredefined.hh>
      77             : #include <EnergyPlus/Psychrometrics.hh>
      78             : #include <EnergyPlus/ScheduleManager.hh>
      79             : #include <EnergyPlus/UtilityRoutines.hh>
      80             : 
      81             : namespace EnergyPlus::Fans {
      82             : // Module containing the fan simulation routines
      83             : 
      84             : // MODULE INFORMATION:
      85             : //       AUTHOR         Richard J. Liesen
      86             : //       DATE WRITTEN   April 1998
      87             : //       MODIFIED       Shirey, May 2001
      88             : //                      Griffith, May 2009, EMS changes
      89             : //                      Craig Wray 22Aug2010 Added Fan Component Model
      90             : 
      91             : // PURPOSE OF THIS MODULE:
      92             : // To encapsulate the data and algorithms required to
      93             : // manage the Fan System Component
      94             : 
      95             : constexpr std::array<std::string_view, (int)MinFlowFracMethod::Num> minFlowFracMethodNames = {"Fraction", "FixedFlowRate"};
      96             : 
      97             : constexpr std::array<std::string_view, (int)MinFlowFracMethod::Num> minFlowFracMethodNamesUC = {"FRACTION", "FIXEDFLOWRATE"};
      98             : 
      99   156131914 : void FanBase::simulate(EnergyPlusData &state,
     100             :                        bool const _FirstHVACIteration,
     101             :                        ObjexxFCL::Optional<Real64 const> _speedRatio, // SpeedRatio for Fan:SystemModel
     102             : 
     103             :                        // = current flow/ max design flow rate.  It is not exactly the same as
     104             :                        // the legacy speed ratio that was used with SimulateFanComponents.
     105             :                        ObjexxFCL::Optional<Real64 const> _pressureRise, // Pressure difference to use for DeltaPress, for rating DX coils at a
     106             :                        ObjexxFCL::Optional<Real64 const> _flowFraction, // when used, this directs the fan to set the flow at this flow fraction
     107             :                        // different pressure without entire duct system
     108             :                        ObjexxFCL::Optional<Real64 const> _massFlowRate1,    // Mass flow rate in operating mode 1 [kg/s]
     109             :                        ObjexxFCL::Optional<Real64 const> _runTimeFraction1, // Run time fraction in operating mode 1
     110             :                        ObjexxFCL::Optional<Real64 const> _massFlowRate2,    // Mass flow rate in operating mode 2 [kg/s]
     111             :                        ObjexxFCL::Optional<Real64 const> _runTimeFraction2, // Run time fraction in opearating mode 2
     112             :                        ObjexxFCL::Optional<Real64 const> _pressureRise2     // Pressure difference for operating mode 2
     113             : )
     114             : {
     115             : 
     116             :     // SUBROUTINE INFORMATION:
     117             :     //       AUTHOR         Richard Liesen
     118             :     //       DATE WRITTEN   February 1998
     119             :     //       MODIFIED       Chandan Sharma, March 2011 - FSEC: Added logic for ZoneHVAC sys avail managers
     120             : 
     121             :     // PURPOSE OF THIS SUBROUTINE:
     122             :     // This subroutine manages Fan component simulation.
     123             : 
     124             :     // With the correct FanNum Initialize
     125   156131914 :     init(state);
     126             : 
     127             :     // It looks like the behavior is different for system and
     128             :     // non-system fans?  Non-system fans can simulate without being
     129             :     // sized first?
     130             : 
     131             :     // Calculate the Correct Fan Model with the current FanNum
     132   156131914 :     if (type != HVAC::FanType::SystemModel) {
     133   145368856 :         auto *_thisFan = dynamic_cast<FanComponent *>(this);
     134   145368856 :         assert(_thisFan != nullptr);
     135             : 
     136   145368856 :         switch (type) {
     137             : 
     138    12106020 :         case HVAC::FanType::Constant: {
     139    12106020 :             _thisFan->simulateConstant(state);
     140    12106020 :         } break;
     141    24416547 :         case HVAC::FanType::VAV: {
     142    24416547 :             _thisFan->simulateVAV(state, _pressureRise);
     143    24416547 :         } break;
     144   107240192 :         case HVAC::FanType::OnOff: {
     145   107240192 :             _thisFan->simulateOnOff(state, _speedRatio);
     146   107240192 :         } break;
     147     1543429 :         case HVAC::FanType::Exhaust: {
     148     1543429 :             _thisFan->simulateZoneExhaust(state);
     149     1543429 :         } break;
     150       62668 :         case HVAC::FanType::ComponentModel: {
     151       62668 :             _thisFan->simulateComponentModel(state);
     152       62668 :         } break;
     153           0 :         default: {
     154           0 :         } break;
     155             :         } // switch (type)
     156             : 
     157             :     } else { // FanType::SystemModel
     158             : 
     159    10763058 :         if (sizingFlag) return;
     160             : 
     161    10762717 :         auto *_thisFan = dynamic_cast<FanSystem *>(this);
     162    10762717 :         assert(_thisFan != nullptr);
     163             : 
     164    10767345 :         if (present(_pressureRise) && present(_massFlowRate1) && present(_runTimeFraction1) && present(_massFlowRate2) &&
     165    10767345 :             present(_runTimeFraction2) && present(_pressureRise2)) {
     166           0 :             Real64 _flowRatio1 = _massFlowRate1 / maxAirMassFlowRate;
     167           0 :             Real64 _flowRatio2 = _massFlowRate2 / maxAirMassFlowRate;
     168           0 :             _thisFan->calcSimpleSystemFan(state, _, _pressureRise, _flowRatio1, _runTimeFraction1, _flowRatio2, _runTimeFraction2, _pressureRise2);
     169    21520806 :         } else if (!present(_pressureRise) && present(_massFlowRate1) && present(_runTimeFraction1) && present(_massFlowRate2) &&
     170    21520806 :                    present(_runTimeFraction2) && !present(_pressureRise2)) {
     171     5909487 :             Real64 _flowRatio1 = _massFlowRate1 / maxAirMassFlowRate;
     172     5909487 :             Real64 _flowRatio2 = _massFlowRate2 / maxAirMassFlowRate;
     173     5909487 :             _thisFan->calcSimpleSystemFan(state, _flowFraction, _, _flowRatio1, _runTimeFraction1, _flowRatio2, _runTimeFraction2, _);
     174     4853230 :         } else if (present(_pressureRise) && present(_flowFraction)) {
     175           0 :             _thisFan->calcSimpleSystemFan(state, _flowFraction, _pressureRise, _, _, _, _, _);
     176     4853230 :         } else if (present(_pressureRise) && !present(_flowFraction)) {
     177        4628 :             _thisFan->calcSimpleSystemFan(state, _, _pressureRise, _, _, _, _, _);
     178     4848602 :         } else if (!present(_pressureRise) && present(_flowFraction)) {
     179     1321323 :             _thisFan->calcSimpleSystemFan(state, _flowFraction, _, _, _, _, _, _);
     180             :         } else {
     181     3527279 :             _thisFan->calcSimpleSystemFan(state, _, _, _, _, _, _, _);
     182             :         }
     183             :     }
     184             : 
     185   156131573 :     update(state);
     186   156131573 :     report(state);
     187             : }
     188             : 
     189             : // Get Input Section of the Module
     190             : //******************************************************************************
     191             : 
     192         596 : void GetFanInput(EnergyPlusData &state)
     193             : {
     194             : 
     195             :     // SUBROUTINE INFORMATION:
     196             :     //       AUTHOR         Richard Liesen
     197             :     //       DATE WRITTEN   April 1998
     198             :     //       MODIFIED       Shirey, May 2001
     199             : 
     200             :     // PURPOSE OF THIS SUBROUTINE:
     201             :     // Obtains input data for fans and stores it in fan data structures
     202             : 
     203             :     static constexpr std::string_view routineName = "GetFanInput";
     204             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     205             :     int NumAlphas;
     206             :     int NumNums;
     207             :     int IOStat;
     208         596 :     bool ErrorsFound(false); // If errors detected in input
     209         596 :     Array1D_string cAlphaFieldNames;
     210         596 :     Array1D_string cNumericFieldNames;
     211         596 :     Array1D_bool lNumericFieldBlanks;
     212         596 :     Array1D_bool lAlphaFieldBlanks;
     213         596 :     Array1D_string cAlphaArgs;
     214         596 :     Array1D<Real64> rNumericArgs;
     215         596 :     std::string cCurrentModuleObject;
     216             :     int NumParams;
     217             : 
     218         596 :     auto &ip = state.dataInputProcessing->inputProcessor;
     219         596 :     auto &df = state.dataFans;
     220             : 
     221         596 :     state.dataFans->GetFanInputFlag = false;
     222             : 
     223         596 :     int MaxAlphas = 0;
     224         596 :     int MaxNumbers = 0;
     225         596 :     int NumSimpFan = ip->getNumObjectsFound(state, "Fan:ConstantVolume");
     226         596 :     if (NumSimpFan > 0) {
     227         206 :         ip->getObjectDefMaxArgs(state, "Fan:ConstantVolume", NumParams, NumAlphas, NumNums);
     228         206 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     229         206 :         MaxNumbers = max(MaxNumbers, NumNums);
     230             :     }
     231         596 :     int NumVarVolFan = ip->getNumObjectsFound(state, "Fan:VariableVolume");
     232         596 :     if (NumVarVolFan > 0) {
     233         260 :         ip->getObjectDefMaxArgs(state, "Fan:VariableVolume", NumParams, NumAlphas, NumNums);
     234         260 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     235         260 :         MaxNumbers = max(MaxNumbers, NumNums);
     236             :     }
     237         596 :     int NumOnOff = ip->getNumObjectsFound(state, "Fan:OnOff");
     238         596 :     if (NumOnOff > 0) {
     239         173 :         ip->getObjectDefMaxArgs(state, "Fan:OnOff", NumParams, NumAlphas, NumNums);
     240         173 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     241         173 :         MaxNumbers = max(MaxNumbers, NumNums);
     242             :     }
     243         596 :     int NumZoneExhFan = ip->getNumObjectsFound(state, "Fan:ZoneExhaust");
     244         596 :     if (NumZoneExhFan > 0) {
     245          45 :         ip->getObjectDefMaxArgs(state, "Fan:ZoneExhaust", NumParams, NumAlphas, NumNums);
     246          45 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     247          45 :         MaxNumbers = max(MaxNumbers, NumNums);
     248             :     }
     249         596 :     state.dataFans->NumNightVentPerf = ip->getNumObjectsFound(state, "FanPerformance:NightVentilation");
     250         596 :     if (df->NumNightVentPerf > 0) {
     251           0 :         ip->getObjectDefMaxArgs(state, "FanPerformance:NightVentilation", NumParams, NumAlphas, NumNums);
     252           0 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     253           0 :         MaxNumbers = max(MaxNumbers, NumNums);
     254             :     }
     255             : 
     256         596 :     int NumCompModelFan = ip->getNumObjectsFound(state, "Fan:ComponentModel");
     257         596 :     if (NumCompModelFan > 0) {
     258           5 :         ip->getObjectDefMaxArgs(state, "Fan:ComponentModel", NumParams, NumAlphas, NumNums);
     259           5 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     260           5 :         MaxNumbers = max(MaxNumbers, NumNums);
     261             :     }
     262             : 
     263         596 :     int NumSystemModelFan = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Fan:SystemModel");
     264         596 :     if (NumSystemModelFan > 0) {
     265          66 :         ip->getObjectDefMaxArgs(state, "Fan:SystemModel", NumParams, NumAlphas, NumNums);
     266          66 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     267          66 :         MaxNumbers = max(MaxNumbers, NumNums);
     268             :     }
     269             : 
     270         596 :     cAlphaArgs.allocate(MaxAlphas);
     271         596 :     cAlphaFieldNames.allocate(MaxAlphas);
     272         596 :     lAlphaFieldBlanks.dimension(MaxAlphas, false);
     273         596 :     cNumericFieldNames.allocate(MaxNumbers);
     274         596 :     lNumericFieldBlanks.dimension(MaxNumbers, false);
     275         596 :     rNumericArgs.dimension(MaxNumbers, 0.0);
     276             : 
     277        1120 :     for (int SimpFanNum = 1; SimpFanNum <= NumSimpFan; ++SimpFanNum) {
     278         524 :         cCurrentModuleObject = "Fan:ConstantVolume";
     279         524 :         ip->getObjectItem(state,
     280             :                           cCurrentModuleObject,
     281             :                           SimpFanNum,
     282             :                           cAlphaArgs,
     283             :                           NumAlphas,
     284             :                           rNumericArgs,
     285             :                           NumNums,
     286             :                           IOStat,
     287             :                           lNumericFieldBlanks,
     288             :                           lAlphaFieldBlanks,
     289             :                           cAlphaFieldNames,
     290             :                           cNumericFieldNames);
     291             : 
     292         524 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     293             : 
     294         524 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     295           0 :             ShowSevereDuplicateName(state, eoh);
     296           0 :             ErrorsFound = true;
     297             :         }
     298             : 
     299         524 :         auto *fan = new FanComponent;
     300         524 :         fan->Name = cAlphaArgs(1);
     301             : 
     302         524 :         df->fans.push_back(fan);
     303         524 :         df->fanMap.insert_or_assign(cAlphaArgs(1), df->fans.size());
     304             : 
     305         524 :         fan->type = HVAC::FanType::Constant;
     306         524 :         fan->sizingPrefix = cNumericFieldNames(3);
     307             : 
     308         524 :         if (lAlphaFieldBlanks(2)) {
     309           0 :             fan->availSchedNum = ScheduleManager::ScheduleAlwaysOn;
     310         524 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2))) == 0) {
     311           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
     312           0 :             ErrorsFound = true;
     313             :         }
     314             : 
     315         524 :         fan->totalEff = rNumericArgs(1);
     316         524 :         fan->deltaPress = rNumericArgs(2);
     317         524 :         fan->maxAirFlowRate = rNumericArgs(3);
     318         524 :         if (fan->maxAirFlowRate == 0.0) {
     319           0 :             ShowWarningError(
     320             :                 state,
     321           0 :                 format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
     322             :         }
     323         524 :         fan->maxAirFlowRateIsAutosized = true;
     324         524 :         fan->motorEff = rNumericArgs(4);
     325         524 :         fan->motorInAirFrac = rNumericArgs(5);
     326         524 :         fan->minAirFlowRate = 0.0;
     327             : 
     328         524 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     329         524 :                                                                 cAlphaArgs(3),
     330             :                                                                 ErrorsFound,
     331             :                                                                 DataLoopNode::ConnectionObjectType::FanConstantVolume,
     332         524 :                                                                 cAlphaArgs(1),
     333             :                                                                 DataLoopNode::NodeFluidType::Air,
     334             :                                                                 DataLoopNode::ConnectionType::Inlet,
     335             :                                                                 NodeInputManager::CompFluidStream::Primary,
     336             :                                                                 DataLoopNode::ObjectIsNotParent);
     337         524 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     338         524 :                                                                  cAlphaArgs(4),
     339             :                                                                  ErrorsFound,
     340             :                                                                  DataLoopNode::ConnectionObjectType::FanConstantVolume,
     341         524 :                                                                  cAlphaArgs(1),
     342             :                                                                  DataLoopNode::NodeFluidType::Air,
     343             :                                                                  DataLoopNode::ConnectionType::Outlet,
     344             :                                                                  NodeInputManager::CompFluidStream::Primary,
     345             :                                                                  DataLoopNode::ObjectIsNotParent);
     346             : 
     347         524 :         fan->endUseSubcategoryName = (NumAlphas > 4) ? cAlphaArgs(5) : "General";
     348             : 
     349         524 :         BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
     350             : 
     351         524 :         if (ErrorsFound) {
     352           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
     353             :         }
     354             :     } // for (iFanConstant)
     355             : 
     356        1026 :     for (int VarVolFanNum = 1; VarVolFanNum <= NumVarVolFan; ++VarVolFanNum) {
     357         430 :         cCurrentModuleObject = "Fan:VariableVolume";
     358         430 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     359             :                                                                  cCurrentModuleObject,
     360             :                                                                  VarVolFanNum,
     361             :                                                                  cAlphaArgs,
     362             :                                                                  NumAlphas,
     363             :                                                                  rNumericArgs,
     364             :                                                                  NumNums,
     365             :                                                                  IOStat,
     366             :                                                                  lNumericFieldBlanks,
     367             :                                                                  lAlphaFieldBlanks,
     368             :                                                                  cAlphaFieldNames,
     369             :                                                                  cNumericFieldNames);
     370             : 
     371         430 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     372             : 
     373         430 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     374           0 :             ShowSevereDuplicateName(state, eoh);
     375           0 :             ErrorsFound = true;
     376             :         }
     377             : 
     378         430 :         auto *fan = new FanComponent;
     379         430 :         fan->Name = cAlphaArgs(1);
     380             : 
     381         430 :         df->fans.push_back(fan);
     382         430 :         df->fanMap.insert_or_assign(cAlphaArgs(1), df->fans.size());
     383             : 
     384         430 :         fan->type = HVAC::FanType::VAV;
     385         430 :         fan->sizingPrefix = cNumericFieldNames(3);
     386             : 
     387         430 :         if (lAlphaFieldBlanks(2)) {
     388           0 :             fan->availSchedNum = ScheduleManager::ScheduleAlwaysOn;
     389         430 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2))) == 0) {
     390           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
     391           0 :             ErrorsFound = true;
     392             :         }
     393             : 
     394         430 :         fan->totalEff = rNumericArgs(1);
     395         430 :         fan->deltaPress = rNumericArgs(2);
     396         430 :         fan->maxAirFlowRate = rNumericArgs(3);
     397         430 :         if (fan->maxAirFlowRate == 0.0) {
     398           0 :             ShowWarningError(
     399             :                 state,
     400           0 :                 format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
     401             :         }
     402         430 :         fan->maxAirFlowRateIsAutosized = true;
     403         430 :         fan->minAirFracMethod = static_cast<MinFlowFracMethod>(getEnumValue(minFlowFracMethodNamesUC, cAlphaArgs(3)));
     404             : 
     405         430 :         fan->minFrac = rNumericArgs(4);
     406         430 :         fan->fixedMin = rNumericArgs(5);
     407         430 :         fan->motorEff = rNumericArgs(6);
     408         430 :         fan->motorInAirFrac = rNumericArgs(7);
     409         430 :         fan->coeffs[0] = rNumericArgs(8);
     410         430 :         fan->coeffs[1] = rNumericArgs(9);
     411         430 :         fan->coeffs[2] = rNumericArgs(10);
     412         430 :         fan->coeffs[3] = rNumericArgs(11);
     413         430 :         fan->coeffs[4] = rNumericArgs(12);
     414         430 :         if (fan->coeffs[0] == 0.0 && fan->coeffs[1] == 0.0 && fan->coeffs[2] == 0.0 && fan->coeffs[3] == 0.0 && fan->coeffs[4] == 0.0) {
     415           0 :             ShowWarningError(state, "Fan Coefficients are all zero.  No Fan power will be reported.");
     416           0 :             ShowContinueError(state, format("For {}, Fan={}", cCurrentModuleObject, cAlphaArgs(1)));
     417             :         }
     418         430 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     419         430 :                                                                 cAlphaArgs(4),
     420             :                                                                 ErrorsFound,
     421             :                                                                 DataLoopNode::ConnectionObjectType::FanVariableVolume,
     422         430 :                                                                 cAlphaArgs(1),
     423             :                                                                 DataLoopNode::NodeFluidType::Air,
     424             :                                                                 DataLoopNode::ConnectionType::Inlet,
     425             :                                                                 NodeInputManager::CompFluidStream::Primary,
     426             :                                                                 DataLoopNode::ObjectIsNotParent);
     427         430 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     428         430 :                                                                  cAlphaArgs(5),
     429             :                                                                  ErrorsFound,
     430             :                                                                  DataLoopNode::ConnectionObjectType::FanVariableVolume,
     431         430 :                                                                  cAlphaArgs(1),
     432             :                                                                  DataLoopNode::NodeFluidType::Air,
     433             :                                                                  DataLoopNode::ConnectionType::Outlet,
     434             :                                                                  NodeInputManager::CompFluidStream::Primary,
     435             :                                                                  DataLoopNode::ObjectIsNotParent);
     436             : 
     437         430 :         fan->endUseSubcategoryName = (NumAlphas > 5) ? cAlphaArgs(6) : "General";
     438             : 
     439         430 :         BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(4), cAlphaArgs(5), "Air Nodes");
     440             : 
     441         430 :         if (ErrorsFound) {
     442           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
     443             :         }
     444             :     } // for (iFanVAV)
     445             : 
     446         740 :     for (int ExhFanNum = 1; ExhFanNum <= NumZoneExhFan; ++ExhFanNum) {
     447         144 :         cCurrentModuleObject = "Fan:ZoneExhaust";
     448         144 :         ip->getObjectItem(state,
     449             :                           cCurrentModuleObject,
     450             :                           ExhFanNum,
     451             :                           cAlphaArgs,
     452             :                           NumAlphas,
     453             :                           rNumericArgs,
     454             :                           NumNums,
     455             :                           IOStat,
     456             :                           lNumericFieldBlanks,
     457             :                           lAlphaFieldBlanks,
     458             :                           cAlphaFieldNames,
     459             :                           cNumericFieldNames);
     460             : 
     461         144 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     462             : 
     463         144 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     464           0 :             ShowSevereDuplicateName(state, eoh);
     465           0 :             ErrorsFound = true;
     466             :         }
     467             : 
     468         144 :         auto *fan = new FanComponent;
     469         144 :         fan->Name = cAlphaArgs(1);
     470             : 
     471         144 :         df->fans.push_back(fan);
     472         144 :         df->fanMap.insert_or_assign(fan->Name, df->fans.size());
     473             : 
     474         144 :         fan->type = HVAC::FanType::Exhaust;
     475         144 :         fan->sizingPrefix = cNumericFieldNames(3);
     476             : 
     477         144 :         if (lAlphaFieldBlanks(2)) {
     478           1 :             fan->availSchedNum = ScheduleManager::ScheduleAlwaysOn;
     479         143 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2))) == 0) {
     480           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
     481           0 :             ErrorsFound = true;
     482         143 :         } else if (ScheduleManager::HasFractionalScheduleValue(state, fan->availSchedNum)) {
     483           0 :             ShowWarningError(state,
     484           0 :                              format("{}=\"{}\" has fractional values in Schedule={}. Only 0.0 in the schedule value turns the fan off.",
     485             :                                     cCurrentModuleObject,
     486           0 :                                     fan->Name,
     487             :                                     cAlphaArgs(2)));
     488             :         }
     489             : 
     490         144 :         fan->totalEff = rNumericArgs(1);
     491         144 :         fan->deltaPress = rNumericArgs(2);
     492         144 :         fan->maxAirFlowRate = rNumericArgs(3);
     493         144 :         fan->maxAirFlowRateIsAutosized = false;
     494         144 :         fan->motorEff = 1.0;
     495         144 :         fan->motorInAirFrac = 1.0;
     496         144 :         fan->minAirFlowRate = 0.0;
     497         144 :         fan->rhoAirStdInit = state.dataEnvrn->StdRhoAir;
     498         144 :         fan->maxAirMassFlowRate = fan->maxAirFlowRate * fan->rhoAirStdInit;
     499             : 
     500         144 :         if (fan->maxAirFlowRate == 0.0) {
     501           0 :             ShowWarningError(
     502             :                 state,
     503           0 :                 format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
     504             :         }
     505             : 
     506         144 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     507         144 :                                                                 cAlphaArgs(3),
     508             :                                                                 ErrorsFound,
     509             :                                                                 DataLoopNode::ConnectionObjectType::FanZoneExhaust,
     510         144 :                                                                 cAlphaArgs(1),
     511             :                                                                 DataLoopNode::NodeFluidType::Air,
     512             :                                                                 DataLoopNode::ConnectionType::Inlet,
     513             :                                                                 NodeInputManager::CompFluidStream::Primary,
     514             :                                                                 DataLoopNode::ObjectIsNotParent);
     515         144 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     516         144 :                                                                  cAlphaArgs(4),
     517             :                                                                  ErrorsFound,
     518             :                                                                  DataLoopNode::ConnectionObjectType::FanZoneExhaust,
     519         144 :                                                                  cAlphaArgs(1),
     520             :                                                                  DataLoopNode::NodeFluidType::Air,
     521             :                                                                  DataLoopNode::ConnectionType::Outlet,
     522             :                                                                  NodeInputManager::CompFluidStream::Primary,
     523             :                                                                  DataLoopNode::ObjectIsNotParent);
     524             : 
     525         284 :         fan->endUseSubcategoryName = (NumAlphas > 4 && !lAlphaFieldBlanks(5)) ? cAlphaArgs(5) : "General";
     526             : 
     527         144 :         if (NumAlphas <= 5 || lAlphaFieldBlanks(6)) {
     528         144 :             fan->flowFracSchedNum = ScheduleManager::ScheduleAlwaysOn;
     529           0 :         } else if ((fan->flowFracSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(6))) == 0) {
     530           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(6), cAlphaArgs(6));
     531           0 :             ErrorsFound = true;
     532           0 :         } else if (!ScheduleManager::CheckScheduleValueMinMax(
     533             :                        state, fan->flowFracSchedNum, ScheduleManager::Clusivity::Inclusive, 0.0, ScheduleManager::Clusivity::Inclusive, 1.0)) {
     534           0 :             ShowSevereError(
     535             :                 state,
     536           0 :                 format("{}: {}: invalid {} for {}={}", routineName, cCurrentModuleObject, cAlphaFieldNames(6), cAlphaFieldNames(1), cAlphaArgs(1)));
     537           0 :             ShowContinueError(state, format("Error found in {} = {}", cAlphaFieldNames(6), cAlphaArgs(6)));
     538           0 :             ShowContinueError(state, "Schedule values must be (>=0., <=1.)");
     539           0 :             ErrorsFound = true;
     540             :         }
     541             : 
     542         144 :         if (NumAlphas <= 6 || lAlphaFieldBlanks(7)) {
     543         137 :             fan->availManagerMode = AvailManagerMode::Coupled;
     544           7 :         } else if ((fan->availManagerMode = static_cast<AvailManagerMode>(getEnumValue(availManagerModeNamesUC, cAlphaArgs(7)))) ==
     545             :                    AvailManagerMode::Invalid) {
     546           0 :             ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(7), cAlphaArgs(7));
     547           0 :             ErrorsFound = true;
     548             :         }
     549             : 
     550         144 :         if (NumAlphas <= 7 || lAlphaFieldBlanks(8)) {
     551         144 :             fan->minTempLimitSchedNum = 0;
     552           0 :         } else if ((fan->minTempLimitSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(8))) == 0) {
     553           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(8), cAlphaArgs(8));
     554           0 :             ErrorsFound = true;
     555             :         }
     556             : 
     557         144 :         if (NumAlphas <= 8 || lAlphaFieldBlanks(9)) {
     558         112 :             fan->balancedFractSchedNum = 0;
     559          32 :         } else if (state.dataHeatBal->ZoneAirMassFlow.ZoneFlowAdjustment != DataHeatBalance::AdjustmentType::NoAdjustReturnAndMixing) {
     560             :             // do not include adjusted for "balanced" exhaust flow in the zone total return calculation
     561           0 :             ShowWarningError(state,
     562           0 :                              format("{}: {}: invalid {} = {} for {}={}",
     563             :                                     routineName,
     564             :                                     cCurrentModuleObject,
     565             :                                     cAlphaFieldNames(9),
     566             :                                     cAlphaArgs(9),
     567             :                                     cAlphaFieldNames(1),
     568             :                                     cAlphaArgs(1)));
     569           0 :             ShowContinueError(state, "When zone air mass flow balance is enforced, this input field should be left blank.");
     570           0 :             ShowContinueError(state, "This schedule will be ignored in the simulation.");
     571           0 :             fan->balancedFractSchedNum = 0;
     572          32 :         } else if ((fan->balancedFractSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(9))) == 0) {
     573           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(9), cAlphaArgs(9));
     574           0 :             ErrorsFound = true;
     575          32 :         } else if (!ScheduleManager::CheckScheduleValueMinMax(
     576             :                        state, fan->balancedFractSchedNum, ScheduleManager::Clusivity::Inclusive, 0.0, ScheduleManager::Clusivity::Inclusive, 1.0)) {
     577           0 :             ShowSevereError(
     578             :                 state,
     579           0 :                 format("{}: {}: invalid {} for {}={}", routineName, cCurrentModuleObject, cAlphaFieldNames(9), cAlphaFieldNames(1), cAlphaArgs(1)));
     580           0 :             ShowContinueError(state, format("Error found in {} = {}", cAlphaFieldNames(9), cAlphaArgs(9)));
     581           0 :             ShowContinueError(state, "Schedule values must be (>=0., <=1.)");
     582           0 :             ErrorsFound = true;
     583             :         }
     584         144 :         if (ErrorsFound) {
     585           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
     586             :         }
     587             :     } // for (iFanExhaust)
     588             : 
     589        1478 :     for (int OnOffFanNum = 1; OnOffFanNum <= NumOnOff; ++OnOffFanNum) {
     590         882 :         cCurrentModuleObject = "Fan:OnOff";
     591         882 :         ip->getObjectItem(state,
     592             :                           cCurrentModuleObject,
     593             :                           OnOffFanNum,
     594             :                           cAlphaArgs,
     595             :                           NumAlphas,
     596             :                           rNumericArgs,
     597             :                           NumNums,
     598             :                           IOStat,
     599             :                           lNumericFieldBlanks,
     600             :                           lAlphaFieldBlanks,
     601             :                           cAlphaFieldNames,
     602             :                           cNumericFieldNames);
     603             : 
     604         882 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     605             : 
     606         882 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     607           0 :             ShowSevereDuplicateName(state, eoh);
     608           0 :             ErrorsFound = true;
     609             :         }
     610             : 
     611         882 :         auto *fan = new FanComponent;
     612         882 :         fan->Name = cAlphaArgs(1);
     613             : 
     614         882 :         df->fans.push_back(fan);
     615         882 :         df->fanMap.insert_or_assign(fan->Name, df->fans.size());
     616             : 
     617         882 :         fan->type = HVAC::FanType::OnOff;
     618         882 :         fan->sizingPrefix = cNumericFieldNames(3);
     619             : 
     620         882 :         if (lAlphaFieldBlanks(2)) {
     621           0 :             fan->availSchedNum = ScheduleManager::ScheduleAlwaysOn;
     622         882 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2))) == 0) {
     623           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
     624           0 :             ErrorsFound = true;
     625             :         }
     626             : 
     627         882 :         fan->totalEff = rNumericArgs(1);
     628         882 :         fan->deltaPress = rNumericArgs(2);
     629         882 :         fan->maxAirFlowRate = rNumericArgs(3);
     630         882 :         if (fan->maxAirFlowRate == 0.0) {
     631           0 :             ShowWarningError(
     632             :                 state,
     633           0 :                 format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
     634             :         }
     635         882 :         fan->maxAirFlowRateIsAutosized = true;
     636             :         //       the following two structure variables are set here, as well as in InitFan, for the Heat Pump:Water Heater object
     637             :         //       (Standard Rating procedure may be called before BeginEnvirFlag is set to TRUE, if so MaxAirMassFlowRate = 0)
     638         882 :         fan->rhoAirStdInit = state.dataEnvrn->StdRhoAir;
     639         882 :         fan->maxAirMassFlowRate = fan->maxAirFlowRate * fan->rhoAirStdInit;
     640             : 
     641         882 :         fan->motorEff = rNumericArgs(4);
     642         882 :         fan->motorInAirFrac = rNumericArgs(5);
     643         882 :         fan->minAirFlowRate = 0.0;
     644             : 
     645         882 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     646         882 :                                                                 cAlphaArgs(3),
     647             :                                                                 ErrorsFound,
     648             :                                                                 DataLoopNode::ConnectionObjectType::FanOnOff,
     649         882 :                                                                 cAlphaArgs(1),
     650             :                                                                 DataLoopNode::NodeFluidType::Air,
     651             :                                                                 DataLoopNode::ConnectionType::Inlet,
     652             :                                                                 NodeInputManager::CompFluidStream::Primary,
     653             :                                                                 DataLoopNode::ObjectIsNotParent);
     654         882 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     655         882 :                                                                  cAlphaArgs(4),
     656             :                                                                  ErrorsFound,
     657             :                                                                  DataLoopNode::ConnectionObjectType::FanOnOff,
     658         882 :                                                                  cAlphaArgs(1),
     659             :                                                                  DataLoopNode::NodeFluidType::Air,
     660             :                                                                  DataLoopNode::ConnectionType::Outlet,
     661             :                                                                  NodeInputManager::CompFluidStream::Primary,
     662             :                                                                  DataLoopNode::ObjectIsNotParent);
     663             : 
     664         882 :         if (NumAlphas > 4 && !lAlphaFieldBlanks(5)) {
     665          25 :             fan->powerRatioAtSpeedRatioCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(5));
     666             :         }
     667             : 
     668         882 :         if (NumAlphas > 5 && !lAlphaFieldBlanks(6)) {
     669          10 :             fan->effRatioCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(6));
     670             :         }
     671             : 
     672        1267 :         fan->endUseSubcategoryName = (NumAlphas > 6 && !lAlphaFieldBlanks(7)) ? cAlphaArgs(7) : "General";
     673             : 
     674         882 :         BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
     675             : 
     676         882 :         if (ErrorsFound) {
     677           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
     678             :         }
     679             :     } // for (iFanOnOff)
     680             : 
     681         596 :     if (state.dataFans->NumNightVentPerf > 0) {
     682           0 :         df->NightVentPerf.allocate(state.dataFans->NumNightVentPerf);
     683           0 :         for (auto &e : df->NightVentPerf) {
     684           0 :             e.FanName.clear();
     685           0 :             e.FanEff = 0.0;
     686           0 :             e.DeltaPress = 0.0;
     687           0 :             e.MaxAirFlowRate = 0.0;
     688           0 :             e.MotEff = 0.0;
     689           0 :             e.MotInAirFrac = 0.0;
     690           0 :             e.MaxAirMassFlowRate = 0.0;
     691             :         }
     692             : 
     693             :         // input the night ventilation performance objects
     694           0 :         for (int NVPerfNum = 1; NVPerfNum <= state.dataFans->NumNightVentPerf; ++NVPerfNum) {
     695           0 :             cCurrentModuleObject = "FanPerformance:NightVentilation";
     696           0 :             ip->getObjectItem(state,
     697             :                               cCurrentModuleObject,
     698             :                               NVPerfNum,
     699             :                               cAlphaArgs,
     700             :                               NumAlphas,
     701             :                               rNumericArgs,
     702             :                               NumNums,
     703             :                               IOStat,
     704             :                               lNumericFieldBlanks,
     705             :                               lAlphaFieldBlanks,
     706             :                               cAlphaFieldNames,
     707             :                               cNumericFieldNames);
     708             : 
     709           0 :             ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     710             : 
     711           0 :             auto &nvp = df->NightVentPerf(NVPerfNum);
     712             : 
     713           0 :             nvp.FanName = cAlphaArgs(1);
     714           0 :             nvp.FanEff = rNumericArgs(1);
     715           0 :             nvp.DeltaPress = rNumericArgs(2);
     716           0 :             nvp.MaxAirFlowRate = rNumericArgs(3);
     717           0 :             nvp.MotEff = rNumericArgs(4);
     718           0 :             nvp.MotInAirFrac = rNumericArgs(5);
     719             : 
     720           0 :             auto found = df->fanMap.find(nvp.FanName);
     721           0 :             if (found == df->fanMap.end()) {
     722           0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(1), cAlphaArgs(1));
     723           0 :                 ErrorsFound = true;
     724             :             } else {
     725           0 :                 auto *fan = dynamic_cast<FanComponent *>(df->fans(found->second));
     726           0 :                 assert(fan != nullptr);
     727           0 :                 fan->nightVentPerfNum = NVPerfNum;
     728             :             }
     729           0 :         } // for (iNightVent)
     730             :     }
     731             : 
     732         601 :     for (int CompModelFanNum = 1; CompModelFanNum <= NumCompModelFan; ++CompModelFanNum) {
     733           5 :         cCurrentModuleObject = "Fan:ComponentModel";
     734           5 :         ip->getObjectItem(state,
     735             :                           cCurrentModuleObject,
     736             :                           CompModelFanNum,
     737             :                           cAlphaArgs,
     738             :                           NumAlphas,
     739             :                           rNumericArgs,
     740             :                           NumNums,
     741             :                           IOStat,
     742             :                           lNumericFieldBlanks,
     743             :                           lAlphaFieldBlanks,
     744             :                           cAlphaFieldNames,
     745             :                           cNumericFieldNames);
     746             : 
     747           5 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     748             : 
     749           5 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     750           0 :             ShowSevereDuplicateName(state, eoh);
     751           0 :             ErrorsFound = true;
     752             :         }
     753             : 
     754           5 :         auto *fan = new FanComponent;
     755           5 :         fan->Name = cAlphaArgs(1); // Fan name
     756             : 
     757           5 :         df->fans.push_back(fan);
     758           5 :         df->fanMap.insert_or_assign(fan->Name, df->fans.size());
     759             : 
     760           5 :         fan->type = HVAC::FanType::ComponentModel;
     761           5 :         fan->sizingPrefix = cNumericFieldNames(1);
     762             : 
     763           5 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     764           5 :                                                                 cAlphaArgs(2),
     765             :                                                                 ErrorsFound,
     766             :                                                                 DataLoopNode::ConnectionObjectType::FanComponentModel,
     767           5 :                                                                 cAlphaArgs(1),
     768             :                                                                 DataLoopNode::NodeFluidType::Air,
     769             :                                                                 DataLoopNode::ConnectionType::Inlet,
     770             :                                                                 NodeInputManager::CompFluidStream::Primary,
     771             :                                                                 DataLoopNode::ObjectIsNotParent); // Air inlet node name
     772           5 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     773           5 :                                                                  cAlphaArgs(3),
     774             :                                                                  ErrorsFound,
     775             :                                                                  DataLoopNode::ConnectionObjectType::FanComponentModel,
     776           5 :                                                                  cAlphaArgs(1),
     777             :                                                                  DataLoopNode::NodeFluidType::Air,
     778             :                                                                  DataLoopNode::ConnectionType::Outlet,
     779             :                                                                  NodeInputManager::CompFluidStream::Primary,
     780             :                                                                  DataLoopNode::ObjectIsNotParent); // Air outlet node name
     781             : 
     782           5 :         BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(2), cAlphaArgs(3), "Air Nodes");
     783             : 
     784           5 :         if (lAlphaFieldBlanks(4)) {
     785           0 :             fan->availSchedNum = 0;
     786           5 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(4))) == 0) {
     787           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(4), cAlphaArgs(4));
     788           0 :             ErrorsFound = true;
     789             :         }
     790             : 
     791           5 :         fan->maxAirFlowRate = rNumericArgs(1);
     792           5 :         if (fan->maxAirFlowRate == 0.0) {
     793           0 :             ShowWarningError(
     794             :                 state,
     795           0 :                 format("{}=\"{}\" has specified 0.0 max air flow rate. It will not be used in the simulation.", cCurrentModuleObject, fan->Name));
     796             :         }
     797           5 :         fan->maxAirFlowRateIsAutosized = true;
     798           5 :         fan->minAirFlowRate = rNumericArgs(2);
     799             : 
     800           5 :         fan->sizingFactor = rNumericArgs(3);       // Fan max airflow sizing factor [-]
     801           5 :         fan->wheelDia = rNumericArgs(4);           // Fan wheel outer diameter [m]
     802           5 :         fan->outletArea = rNumericArgs(5);         // Fan outlet area [m2]
     803           5 :         fan->maxEff = rNumericArgs(6);             // Fan maximum static efficiency [-]
     804           5 :         fan->eulerMaxEff = rNumericArgs(7);        // Euler number at Fan maximum static efficiency [-]
     805           5 :         fan->maxDimFlow = rNumericArgs(8);         // Fan maximum dimensionless airflow [-]
     806           5 :         fan->pulleyDiaRatio = rNumericArgs(9);     // Motor/fan pulley diameter ratio [-]
     807           5 :         fan->beltMaxTorque = rNumericArgs(10);     // Belt maximum torque [N-m, autosizable]
     808           5 :         fan->beltSizingFactor = rNumericArgs(11);  // Belt sizing factor [-]
     809           5 :         fan->beltTorqueTrans = rNumericArgs(12);   // Belt fractional torque transition Region 1-2 [-]
     810           5 :         fan->motorMaxSpeed = rNumericArgs(13);     // Motor maximum speed [rpm]
     811           5 :         fan->motorMaxOutPower = rNumericArgs(14);  // Motor maximum output power [W, autosizable]
     812           5 :         fan->motorSizingFactor = rNumericArgs(15); // Motor sizing factor [-]
     813           5 :         fan->motorInAirFrac = rNumericArgs(16);    // Fraction of fan and motor losses to airstream [-]
     814             : 
     815           5 :         fan->vfdEffType = static_cast<VFDEffType>(getEnumValue(vfdEffTypeNamesUC, cAlphaArgs(5))); // VFD efficiency type [Speed or Power
     816             : 
     817           5 :         fan->vfdMaxOutPower = rNumericArgs(17);  // VFD maximum output power [W, autosizable]
     818           5 :         fan->vfdSizingFactor = rNumericArgs(18); // VFD sizing factor [-]
     819             : 
     820             :         // Do we need to do error checking on these curve names?
     821           5 :         if (lAlphaFieldBlanks(6)) {
     822           0 :             fan->pressRiseCurveNum = 0;
     823           5 :         } else if ((fan->pressRiseCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(6))) == 0) { // Fan pressure rise curve
     824           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(6), cAlphaArgs(6));
     825           0 :             ErrorsFound = true;
     826             :         }
     827             : 
     828           5 :         fan->pressResetCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(7));      // Duct static pressure reset curve
     829           5 :         fan->plTotalEffNormCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(8));  // Fan part-load eff (normal) curve
     830           5 :         fan->plTotalEffStallCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(9)); // Fan part-load eff (stall) curve
     831           5 :         fan->dimFlowNormCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(10));    // Fan dim airflow (normal) curve
     832           5 :         fan->dimFlowStallCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(11));   // Fan dim airflow (stall) curve
     833           5 :         fan->beltMaxEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(12));     // Belt max eff curve
     834           5 :         fan->plBeltEffReg1CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(13));  // Belt part-load eff Region 1 curve
     835           5 :         fan->plBeltEffReg2CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(14));  // Belt part-load eff Region 2 curve
     836           5 :         fan->plBeltEffReg3CurveNum = Curve::GetCurveIndex(state, cAlphaArgs(15));  // Belt part-load eff Region 3 curve
     837           5 :         fan->motorMaxEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(16));    // Motor max eff curve
     838           5 :         fan->plMotorEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(17));     // Motor part-load eff curve
     839           5 :         fan->vfdEffCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(18));         // VFD eff curve
     840             : 
     841           5 :         fan->endUseSubcategoryName = (NumAlphas > 18) ? cAlphaArgs(19) : "General";
     842             : 
     843           5 :         if (ErrorsFound) {
     844           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
     845             :         }
     846             :     } // end Number of Component Model FAN Loop
     847             : 
     848         825 :     for (int SystemFanNum = 1; SystemFanNum <= NumSystemModelFan; ++SystemFanNum) {
     849         229 :         constexpr std::string_view cCurrentModuleObject = "Fan:SystemModel";
     850             : 
     851         229 :         ip->getObjectItem(state,
     852             :                           cCurrentModuleObject,
     853             :                           SystemFanNum,
     854             :                           cAlphaArgs,
     855             :                           NumAlphas,
     856             :                           rNumericArgs,
     857             :                           NumNums,
     858             :                           IOStat,
     859             :                           lNumericFieldBlanks,
     860             :                           lAlphaFieldBlanks,
     861             :                           cAlphaFieldNames,
     862             :                           cNumericFieldNames);
     863             : 
     864         229 :         ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     865             : 
     866         229 :         if (df->fanMap.find(cAlphaArgs(1)) != df->fanMap.end()) {
     867           0 :             ShowSevereDuplicateName(state, eoh);
     868           0 :             ErrorsFound = true;
     869             :         }
     870             : 
     871         229 :         auto *fan = new FanSystem;
     872         229 :         fan->Name = cAlphaArgs(1);
     873             : 
     874         229 :         df->fans.push_back(fan);
     875         229 :         df->fanMap.insert_or_assign(fan->Name, df->fans.size());
     876             : 
     877         229 :         fan->type = HVAC::FanType::SystemModel;
     878             : 
     879         229 :         if (lAlphaFieldBlanks(2)) {
     880           4 :             fan->availSchedNum = ScheduleManager::ScheduleAlwaysOn;
     881         225 :         } else if ((fan->availSchedNum = ScheduleManager::GetScheduleIndex(state, cAlphaArgs(2))) == 0) {
     882           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(2), cAlphaArgs(2));
     883           0 :             ErrorsFound = true;
     884             :         }
     885             : 
     886         229 :         fan->inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     887         229 :                                                                 cAlphaArgs(3),
     888             :                                                                 ErrorsFound,
     889             :                                                                 DataLoopNode::ConnectionObjectType::FanSystemModel,
     890         229 :                                                                 cAlphaArgs(1),
     891             :                                                                 DataLoopNode::NodeFluidType::Air,
     892             :                                                                 DataLoopNode::ConnectionType::Inlet,
     893             :                                                                 NodeInputManager::CompFluidStream::Primary,
     894             :                                                                 DataLoopNode::ObjectIsNotParent);
     895         229 :         fan->outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     896         229 :                                                                  cAlphaArgs(4),
     897             :                                                                  ErrorsFound,
     898             :                                                                  DataLoopNode::ConnectionObjectType::FanSystemModel,
     899         229 :                                                                  cAlphaArgs(1),
     900             :                                                                  DataLoopNode::NodeFluidType::Air,
     901             :                                                                  DataLoopNode::ConnectionType::Outlet,
     902             :                                                                  NodeInputManager::CompFluidStream::Primary,
     903             :                                                                  DataLoopNode::ObjectIsNotParent);
     904             : 
     905         229 :         BranchNodeConnections::TestCompSet(state, cCurrentModuleObject, cAlphaArgs(1), cAlphaArgs(3), cAlphaArgs(4), "Air Nodes");
     906             : 
     907         229 :         fan->maxAirFlowRate = rNumericArgs(1);
     908         229 :         if (fan->maxAirFlowRate == DataSizing::AutoSize) {
     909         183 :             fan->maxAirFlowRateIsAutosized = true;
     910             :         }
     911             : 
     912         229 :         if (lAlphaFieldBlanks(5)) {
     913           0 :             fan->speedControl = SpeedControl::Discrete;
     914             :         } else {
     915         229 :             fan->speedControl = static_cast<SpeedControl>(getEnumValue(speedControlNamesUC, cAlphaArgs(5)));
     916             :         }
     917             : 
     918         229 :         fan->minPowerFlowFrac = rNumericArgs(2);
     919         229 :         fan->deltaPress = rNumericArgs(3);
     920         229 :         if (fan->deltaPress <= 0.0) {
     921           0 :             ShowSevereError(state, format("{}: {} zero or negative, invalid entry in {}", routineName, cCurrentModuleObject, cNumericFieldNames(3)));
     922           0 :             ErrorsFound = true;
     923             :         }
     924         229 :         fan->motorEff = rNumericArgs(4);
     925         229 :         fan->motorInAirFrac = rNumericArgs(5);
     926         229 :         fan->designElecPower = rNumericArgs(6);
     927         229 :         if (fan->designElecPower == DataSizing::AutoSize) {
     928         228 :             fan->designElecPowerWasAutosized = true;
     929             :         }
     930         229 :         if (fan->designElecPowerWasAutosized) {
     931         228 :             if (lAlphaFieldBlanks(6)) {
     932           0 :                 fan->powerSizingMethod = PowerSizing::PerFlowPerPressure;
     933             :             } else {
     934         228 :                 fan->powerSizingMethod = static_cast<PowerSizing>(getEnumValue(powerSizingNamesUC, cAlphaArgs(6)));
     935             :             }
     936         228 :             fan->elecPowerPerFlowRate = rNumericArgs(7);
     937         228 :             fan->elecPowerPerFlowRatePerPressure = rNumericArgs(8);
     938         228 :             fan->totalEff = rNumericArgs(9);
     939             :         }
     940             : 
     941         229 :         if (lAlphaFieldBlanks(7)) {
     942         169 :             if (fan->speedControl == SpeedControl::Continuous) {
     943           0 :                 ShowWarningError(state, format("{}{}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
     944           0 :                 ShowContinueError(state,
     945           0 :                                   format("Continuous speed control requires a fan power curve in {} = {}", cAlphaFieldNames(7), cAlphaArgs(7)));
     946           0 :                 ErrorsFound = true;
     947             :             }
     948          60 :         } else if ((fan->powerModFuncFlowFracCurveNum = Curve::GetCurveIndex(state, cAlphaArgs(7))) == 0) {
     949           0 :             ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(7), cAlphaArgs(7));
     950           0 :             if (fan->speedControl == SpeedControl::Continuous) {
     951           0 :                 ErrorsFound = true;
     952             :             }
     953             :         }
     954             : 
     955         229 :         fan->nightVentPressureDelta = rNumericArgs(10);
     956         229 :         fan->nightVentFlowFraction = rNumericArgs(11); // not used
     957             : 
     958         229 :         if (lAlphaFieldBlanks(8)) {
     959         229 :             fan->heatLossDest = HeatLossDest::Outside;
     960           0 :         } else if ((fan->zoneNum = Util::FindItemInList(cAlphaArgs(8), state.dataHeatBal->Zone)) == 0) {
     961           0 :             fan->heatLossDest = HeatLossDest::Outside;
     962           0 :             ShowWarningItemNotFound(state, eoh, cAlphaFieldNames(8), cAlphaArgs(8), "Fan motor heat losses will not be added to a zone");
     963             :             // continue with simulation but motor losses not sent to a zone.
     964             :         } else {
     965           0 :             fan->heatLossDest = HeatLossDest::Zone;
     966             :         }
     967             : 
     968         229 :         fan->zoneRadFract = rNumericArgs(12);
     969         229 :         if (!lAlphaFieldBlanks(9)) {
     970          96 :             fan->endUseSubcategoryName = cAlphaArgs(9);
     971             :         } else {
     972         133 :             fan->endUseSubcategoryName = "General";
     973             :         }
     974             : 
     975         229 :         if (!lNumericFieldBlanks(13)) {
     976          53 :             fan->numSpeeds = rNumericArgs(13);
     977             :         } else {
     978         176 :             fan->numSpeeds = 1;
     979             :         }
     980             : 
     981         229 :         fan->runtimeFracAtSpeed.resize(fan->numSpeeds, 0.0);
     982         229 :         if (fan->speedControl == SpeedControl::Discrete && fan->numSpeeds > 1) {
     983             :             // should have field sets
     984          22 :             fan->flowFracAtSpeed.resize(fan->numSpeeds, 0.0);
     985          22 :             fan->powerFracAtSpeed.resize(fan->numSpeeds, 0.0);
     986          22 :             fan->powerFracInputAtSpeed.resize(fan->numSpeeds, false);
     987          22 :             if (fan->numSpeeds == ((NumNums - 13) / 2) || fan->numSpeeds == ((NumNums + 1 - 13) / 2)) {
     988          69 :                 for (int loopSet = 0; loopSet < fan->numSpeeds; ++loopSet) {
     989          47 :                     fan->flowFracAtSpeed[loopSet] = rNumericArgs(13 + loopSet * 2 + 1);
     990          47 :                     if (!lNumericFieldBlanks(13 + loopSet * 2 + 2)) {
     991          47 :                         fan->powerFracAtSpeed[loopSet] = rNumericArgs(13 + loopSet * 2 + 2);
     992          47 :                         fan->powerFracInputAtSpeed[loopSet] = true;
     993             :                     } else {
     994           0 :                         fan->powerFracInputAtSpeed[loopSet] = false;
     995             :                     }
     996             :                 }
     997          22 :             } else {
     998             :                 // field set input does not match number of speeds, throw warning
     999           0 :                 ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
    1000           0 :                 ShowContinueError(state, "Fan with Discrete speed control does not have input for speed data that matches the number of speeds.");
    1001           0 :                 ErrorsFound = true;
    1002             :             }
    1003             :             // check that flow fractions are increasing
    1004          22 :             bool increasingOrderError = false;
    1005          47 :             for (int loop = 0; loop < (fan->numSpeeds - 1); ++loop) {
    1006          25 :                 if (fan->flowFracAtSpeed[loop] > fan->flowFracAtSpeed[loop + 1]) {
    1007           0 :                     increasingOrderError = true;
    1008             :                 }
    1009             :             }
    1010          22 :             if (increasingOrderError) {
    1011           0 :                 ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
    1012           0 :                 ShowContinueError(state,
    1013             :                                   "Fan with Discrete speed control and multiple speed levels does not have input with flow fractions arranged in "
    1014             :                                   "increasing order.");
    1015           0 :                 ErrorsFound = true;
    1016             :             }
    1017             :         }
    1018             : 
    1019             :         // check if power curve present when any speeds have no power fraction
    1020         229 :         if (fan->speedControl == SpeedControl::Discrete && fan->numSpeeds > 1 && fan->powerModFuncFlowFracCurveNum == 0) {
    1021          22 :             bool foundMissingPowerFraction = false;
    1022          69 :             for (int loop = 0; loop < fan->numSpeeds; ++loop) {
    1023          47 :                 if (!fan->powerFracInputAtSpeed[loop]) {
    1024           0 :                     foundMissingPowerFraction = true;
    1025             :                 }
    1026             :             }
    1027          22 :             if (foundMissingPowerFraction) {
    1028             :                 // field set input does not match number of speeds, throw warning
    1029           0 :                 ShowSevereError(state, format("{}: {}=\"{}\", invalid entry.", routineName, cCurrentModuleObject, cAlphaArgs(1)));
    1030           0 :                 ShowContinueError(
    1031             :                     state,
    1032             :                     "Fan with Discrete speed control does not have input for power fraction at all speed levels and does not have a power curve.");
    1033           0 :                 ErrorsFound = true;
    1034             :             }
    1035             :         }
    1036             : 
    1037         229 :         if (fan->heatLossDest == HeatLossDest::Zone) {
    1038           0 :             SetupZoneInternalGain(
    1039             :                 state, fan->zoneNum, fan->Name, DataHeatBalance::IntGainType::FanSystemModel, &fan->qdotConvZone, nullptr, &fan->qdotRadZone);
    1040             :         }
    1041         229 :         if (ErrorsFound) {
    1042           0 :             ShowFatalError(state, format("{}: Errors found in input for fan name = {}.  Program terminates.", routineName, fan->Name));
    1043             :         }
    1044             : 
    1045             :     } // for (iFanSystemModel)
    1046             : 
    1047         596 :     cAlphaArgs.deallocate();
    1048         596 :     cAlphaFieldNames.deallocate();
    1049         596 :     lAlphaFieldBlanks.deallocate();
    1050         596 :     cNumericFieldNames.deallocate();
    1051         596 :     lNumericFieldBlanks.deallocate();
    1052         596 :     rNumericArgs.deallocate();
    1053             : 
    1054             :     // Check Fans
    1055             :     // There is a faster way to do this if this gets too slow (doubt it)
    1056        2810 :     for (auto const *fan1 : df->fans) {
    1057       86138 :         for (auto const *fan2 : df->fans) {
    1058       83924 :             if (fan1 == fan2) continue;
    1059             : 
    1060       81710 :             if (fan1->inletNodeNum == fan2->inletNodeNum) {
    1061           0 :                 ErrorsFound = true;
    1062           0 :                 ShowSevereError(state, "GetFanInput, duplicate fan inlet node names, must be unique for fans.");
    1063           0 :                 ShowContinueError(state,
    1064           0 :                                   format("Fan={}:{} and Fan={}:{}.",
    1065           0 :                                          HVAC::fanTypeNames[(int)fan1->type],
    1066           0 :                                          fan1->Name,
    1067           0 :                                          HVAC::fanTypeNames[(int)fan2->type],
    1068           0 :                                          fan2->Name));
    1069           0 :                 ShowContinueError(state, format("Inlet Node Name=\"{}\".", state.dataLoopNodes->NodeID(fan1->inletNodeNum)));
    1070             :             }
    1071       81710 :             if (fan1->outletNodeNum == fan2->outletNodeNum) {
    1072           0 :                 ErrorsFound = true;
    1073           0 :                 ShowSevereError(state, "GetFanInput, duplicate fan outlet node names, must be unique for fans.");
    1074           0 :                 ShowContinueError(state,
    1075           0 :                                   format("Fan={}:{} and Fan={}:{}.",
    1076           0 :                                          HVAC::fanTypeNames[(int)fan1->type],
    1077           0 :                                          fan1->Name,
    1078           0 :                                          HVAC::fanTypeNames[(int)fan2->type],
    1079           0 :                                          fan2->Name));
    1080           0 :                 ShowContinueError(state, format("Outlet Node Name=\"{}\".", state.dataLoopNodes->NodeID(fan1->outletNodeNum)));
    1081             :             }
    1082             :         }
    1083             :     }
    1084             : 
    1085        2810 :     for (auto *fan : df->fans) {
    1086             :         // Setup Report variables for the Fans  CurrentModuleObject='Fans'
    1087        4428 :         SetupOutputVariable(state,
    1088             :                             "Fan Electricity Rate",
    1089             :                             Constant::Units::W,
    1090        2214 :                             fan->totalPower,
    1091             :                             OutputProcessor::TimeStepType::System,
    1092             :                             OutputProcessor::StoreType::Average,
    1093        2214 :                             fan->Name);
    1094        4428 :         SetupOutputVariable(state,
    1095             :                             "Fan Rise in Air Temperature",
    1096             :                             Constant::Units::deltaC,
    1097        2214 :                             fan->deltaTemp,
    1098             :                             OutputProcessor::TimeStepType::System,
    1099             :                             OutputProcessor::StoreType::Average,
    1100        2214 :                             fan->Name);
    1101        4428 :         SetupOutputVariable(state,
    1102             :                             "Fan Heat Gain to Air",
    1103             :                             Constant::Units::W,
    1104        2214 :                             fan->powerLossToAir,
    1105             :                             OutputProcessor::TimeStepType::System,
    1106             :                             OutputProcessor::StoreType::Average,
    1107        2214 :                             fan->Name);
    1108        4428 :         SetupOutputVariable(state,
    1109             :                             "Fan Electricity Energy",
    1110             :                             Constant::Units::J,
    1111        2214 :                             fan->totalEnergy,
    1112             :                             OutputProcessor::TimeStepType::System,
    1113             :                             OutputProcessor::StoreType::Sum,
    1114        2214 :                             fan->Name,
    1115             :                             Constant::eResource::Electricity,
    1116             :                             OutputProcessor::Group::HVAC,
    1117             :                             OutputProcessor::EndUseCat::Fans,
    1118             :                             fan->endUseSubcategoryName);
    1119        4428 :         SetupOutputVariable(state,
    1120             :                             "Fan Air Mass Flow Rate",
    1121             :                             Constant::Units::kg_s,
    1122        2214 :                             fan->outletAirMassFlowRate,
    1123             :                             OutputProcessor::TimeStepType::System,
    1124             :                             OutputProcessor::StoreType::Average,
    1125        2214 :                             fan->Name);
    1126        2214 :         if (fan->type == HVAC::FanType::Exhaust) {
    1127         144 :             auto *fanExhaust = dynamic_cast<FanComponent *>(fan);
    1128         144 :             assert(fanExhaust != nullptr);
    1129         144 :             if (fanExhaust->balancedFractSchedNum > 0) {
    1130          64 :                 SetupOutputVariable(state,
    1131             :                                     "Fan Unbalanced Air Mass Flow Rate",
    1132             :                                     Constant::Units::kg_s,
    1133          32 :                                     fanExhaust->unbalancedOutletMassFlowRate,
    1134             :                                     OutputProcessor::TimeStepType::System,
    1135             :                                     OutputProcessor::StoreType::Average,
    1136          32 :                                     fanExhaust->Name);
    1137          64 :                 SetupOutputVariable(state,
    1138             :                                     "Fan Balanced Air Mass Flow Rate",
    1139             :                                     Constant::Units::kg_s,
    1140          32 :                                     fanExhaust->balancedOutletMassFlowRate,
    1141             :                                     OutputProcessor::TimeStepType::System,
    1142             :                                     OutputProcessor::StoreType::Average,
    1143          32 :                                     fanExhaust->Name);
    1144             :             }
    1145             :         }
    1146             : 
    1147        2214 :         if (fan->type == HVAC::FanType::OnOff) {
    1148         882 :             auto *fanOnOff = dynamic_cast<FanComponent *>(fan);
    1149         882 :             assert(fanOnOff != nullptr);
    1150             : 
    1151        1764 :             SetupOutputVariable(state,
    1152             :                                 "Fan Runtime Fraction",
    1153             :                                 Constant::Units::None,
    1154         882 :                                 fanOnOff->runtimeFrac,
    1155             :                                 OutputProcessor::TimeStepType::System,
    1156             :                                 OutputProcessor::StoreType::Average,
    1157         882 :                                 fanOnOff->Name);
    1158             : 
    1159        1332 :         } else if (fan->type == HVAC::FanType::SystemModel) {
    1160         229 :             auto *fanSystem = dynamic_cast<FanSystem *>(fan);
    1161         229 :             assert(fanSystem != nullptr);
    1162             : 
    1163         229 :             if (fanSystem->speedControl != SpeedControl::Discrete) continue;
    1164             : 
    1165         169 :             if (fanSystem->numSpeeds == 1) {
    1166         294 :                 SetupOutputVariable(state,
    1167             :                                     "Fan Runtime Fraction",
    1168             :                                     Constant::Units::None,
    1169         147 :                                     fanSystem->runtimeFracAtSpeed[0],
    1170             :                                     OutputProcessor::TimeStepType::System,
    1171             :                                     OutputProcessor::StoreType::Average,
    1172         147 :                                     fanSystem->Name);
    1173             :             } else {
    1174          69 :                 for (int speedLoop = 0; speedLoop < fanSystem->numSpeeds; ++speedLoop) {
    1175         141 :                     SetupOutputVariable(state,
    1176          94 :                                         format("Fan Runtime Fraction Speed {}", speedLoop + 1),
    1177             :                                         Constant::Units::None,
    1178          47 :                                         fanSystem->runtimeFracAtSpeed[speedLoop],
    1179             :                                         OutputProcessor::TimeStepType::System,
    1180             :                                         OutputProcessor::StoreType::Average,
    1181          47 :                                         fan->Name);
    1182             :                 }
    1183             :             }
    1184             :         }
    1185             : 
    1186             :     } // for (fan)
    1187             : 
    1188             :     bool anyRan;
    1189         596 :     EMSManager::ManageEMS(state, EMSManager::EMSCallFrom::ComponentGetInput, anyRan, ObjexxFCL::Optional_int_const());
    1190             : 
    1191         596 :     if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
    1192         740 :         for (auto *fan : df->fans) {
    1193         678 :             SetupEMSInternalVariable(state, "Fan Maximum Mass Flow Rate", fan->Name, "[kg/s]", fan->maxAirMassFlowRate);
    1194         678 :             SetupEMSActuator(state, "Fan", fan->Name, "Fan Air Mass Flow Rate", "[kg/s]", fan->EMSMaxMassFlowOverrideOn, fan->EMSAirMassFlowValue);
    1195         678 :             SetupEMSInternalVariable(state, "Fan Nominal Pressure Rise", fan->Name, "[Pa]", fan->deltaPress);
    1196         678 :             SetupEMSActuator(state, "Fan", fan->Name, "Fan Pressure Rise", "[Pa]", fan->EMSPressureOverrideOn, fan->EMSPressureValue);
    1197         678 :             SetupEMSInternalVariable(state, "Fan Nominal Total Efficiency", fan->Name, "[fraction]", fan->totalEff);
    1198         678 :             SetupEMSActuator(state, "Fan", fan->Name, "Fan Total Efficiency", "[fraction]", fan->EMSTotalEffOverrideOn, fan->EMSTotalEffValue);
    1199         678 :             SetupEMSActuator(
    1200         678 :                 state, "Fan", fan->Name, "Fan Autosized Air Flow Rate", "[m3/s]", fan->EMSMaxAirFlowRateOverrideOn, fan->EMSMaxAirFlowRateValue);
    1201             :         }
    1202             :     }
    1203         596 : } // GetFanInput()
    1204             : 
    1205             : // End of Get Input subroutines for the HB Module
    1206             : //******************************************************************************
    1207             : 
    1208             : // Beginning Initialization Section of the Module
    1209             : //******************************************************************************
    1210             : 
    1211   145368856 : void FanComponent::init(EnergyPlusData &state)
    1212             : {
    1213             : 
    1214             :     // SUBROUTINE INFORMATION:
    1215             :     //       AUTHOR         Richard J. Liesen
    1216             :     //       DATE WRITTEN   February 1998
    1217             : 
    1218             :     // PURPOSE OF THIS SUBROUTINE:
    1219             :     // This subroutine is for initializations of the Fan Components.
    1220             : 
    1221             :     // METHODOLOGY EMPLOYED:
    1222             :     // Uses the status flags to trigger initializations.
    1223   145368856 :     auto &df = state.dataFans;
    1224             : 
    1225             :     // need to check all fans to see if they are on Zone Equipment List or issue warning
    1226   145368856 :     if (!state.dataFans->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
    1227         561 :         state.dataFans->ZoneEquipmentListChecked = true;
    1228        2628 :         for (auto *fan : df->fans) {
    1229        2067 :             if (fan->type != HVAC::FanType::Exhaust) continue;
    1230         144 :             if (DataZoneEquipment::CheckZoneEquipmentList(state, HVAC::fanTypeNames[(int)fan->type], fan->Name)) continue;
    1231           0 :             ShowSevereError(state,
    1232           0 :                             format("InitFans: Fan=[{},{}] is not on any ZoneHVAC:EquipmentList.  It will not be simulated.",
    1233           0 :                                    HVAC::fanTypeNames[(int)fan->type],
    1234           0 :                                    fan->Name));
    1235             :         }
    1236             :     }
    1237             : 
    1238   145368856 :     if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
    1239             : 
    1240        1982 :         set_size(state);
    1241             :         // Set the loop cycling flag
    1242        1982 :         if (type == HVAC::FanType::OnOff) {
    1243         880 :             if (state.dataSize->CurSysNum > 0) {
    1244         407 :                 state.dataAirLoop->AirLoopControlInfo(state.dataSize->CurSysNum).CyclingFan = true;
    1245             :             }
    1246             :         }
    1247             : 
    1248        1982 :         sizingFlag = false;
    1249             :     }
    1250             : 
    1251             :     // Do the Begin Environment initializations
    1252   145368856 :     if (state.dataGlobal->BeginEnvrnFlag && envrnFlag) {
    1253             : 
    1254             :         // For all Fan inlet nodes convert the Volume flow to a mass flow
    1255       12346 :         rhoAirStdInit = state.dataEnvrn->StdRhoAir;
    1256             : 
    1257             :         // Change the Volume Flow Rates to Mass Flow Rates
    1258             : 
    1259       12346 :         maxAirMassFlowRate = maxAirFlowRate * rhoAirStdInit;
    1260       12346 :         if (minAirFracMethod == MinFlowFracMethod::MinFrac) {
    1261       11038 :             minAirFlowRate = maxAirFlowRate * minFrac;
    1262       11038 :             minAirMassFlowRate = minAirFlowRate * rhoAirStdInit;
    1263        1308 :         } else if (minAirFracMethod == MinFlowFracMethod::FixedMin) {
    1264        1308 :             minAirFlowRate = fixedMin;
    1265        1308 :             minAirMassFlowRate = minAirFlowRate * rhoAirStdInit;
    1266             :         }
    1267       12346 :         if (nightVentPerfNum > 0) {
    1268           0 :             df->NightVentPerf(nightVentPerfNum).MaxAirMassFlowRate = df->NightVentPerf(nightVentPerfNum).MaxAirFlowRate * rhoAirStdInit;
    1269             :         }
    1270             : 
    1271             :         // Init the Node Control variables
    1272       12346 :         state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax = maxAirMassFlowRate;
    1273             :         // According to the IO Ref guide:
    1274             :         // "Note that this field is only used to calculate the fan power.
    1275             :         // This field does not enforce the system air flow rate during simulation"
    1276             :         // Node(OutNode).MassFlowRateMin = fan.MinAirMassFlowRate;
    1277             : 
    1278             :         // Initialize all report variables to a known state at beginning of simulation
    1279       12346 :         totalPower = 0.0;
    1280       12346 :         deltaTemp = 0.0;
    1281       12346 :         powerLossToAir = 0.0;
    1282       12346 :         totalEnergy = 0.0;
    1283             : 
    1284       12346 :         envrnFlag = false;
    1285             :     }
    1286             : 
    1287   145368856 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    1288   145040000 :         envrnFlag = true;
    1289             :     }
    1290             : 
    1291             :     // Do the following initializations (every time step): This should be the info from
    1292             :     // the previous components outlets or the node data in this section.
    1293             : 
    1294             :     // Do a check and make sure that the max and min available(control) flow is
    1295             :     // between the physical max and min for the Fan while operating.
    1296             : 
    1297   145368856 :     auto const &inletNode = state.dataLoopNodes->Node(inletNodeNum);
    1298   145368856 :     auto const &outletNode = state.dataLoopNodes->Node(outletNodeNum);
    1299             : 
    1300   145368856 :     massFlowRateMaxAvail = min(outletNode.MassFlowRateMax, inletNode.MassFlowRateMaxAvail);
    1301   145368856 :     massFlowRateMinAvail = min(max(outletNode.MassFlowRateMin, inletNode.MassFlowRateMinAvail), inletNode.MassFlowRateMaxAvail);
    1302             : 
    1303             :     // Load the node data in this section for the component simulation
    1304             :     // First need to make sure that the MassFlowRate is between the max and min avail.
    1305   145368856 :     if (type != HVAC::FanType::Exhaust) {
    1306   143825427 :         inletAirMassFlowRate = min(inletNode.MassFlowRate, massFlowRateMaxAvail);
    1307   143825427 :         inletAirMassFlowRate = max(inletAirMassFlowRate, massFlowRateMinAvail);
    1308             :     } else { // zone exhaust fans
    1309     1543429 :         massFlowRateMaxAvail = maxAirMassFlowRate;
    1310     1543429 :         massFlowRateMinAvail = 0.0;
    1311     1543429 :         if (flowFracSchedNum > 0) { // modulate flow
    1312           0 :             inletAirMassFlowRate = massFlowRateMaxAvail * ScheduleManager::GetCurrentScheduleValue(state, flowFracSchedNum);
    1313           0 :             inletAirMassFlowRate = max(0.0, inletAirMassFlowRate);
    1314             :         } else { // always run at max
    1315     1543429 :             inletAirMassFlowRate = massFlowRateMaxAvail;
    1316             :         }
    1317     1543429 :         if (EMSMaxMassFlowOverrideOn) inletAirMassFlowRate = min(EMSAirMassFlowValue, massFlowRateMaxAvail);
    1318             :     }
    1319             : 
    1320             :     // Then set the other conditions
    1321   145368856 :     inletAirTemp = inletNode.Temp;
    1322   145368856 :     inletAirHumRat = inletNode.HumRat;
    1323   145368856 :     inletAirEnthalpy = inletNode.Enthalpy;
    1324   145368856 : }
    1325             : 
    1326        1983 : void FanComponent::set_size(EnergyPlusData &state)
    1327             : {
    1328             : 
    1329             :     // SUBROUTINE INFORMATION:
    1330             :     //       AUTHOR         Fred Buhl
    1331             :     //       DATE WRITTEN   September 2001
    1332             :     //       MODIFIED       Craig Wray August 2010 - added fan, belt, motor, and VFD component sizing
    1333             :     //                      August 2013 Daeho Kang, add component sizing table entries
    1334             : 
    1335             :     // PURPOSE OF THIS SUBROUTINE:
    1336             :     // This subroutine is for sizing fans for which flow rates have not been
    1337             :     // specified in the input, or when fan component sizes have not been specified
    1338             : 
    1339             :     // METHODOLOGY EMPLOYED:
    1340             :     // Obtains flow rates from the zone or system sizing arrays.
    1341             : 
    1342             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1343             :     static constexpr std::string_view routineName = "FanComponent::set_size()"; // include trailing blank space
    1344             : 
    1345             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1346        1983 :     bool _bPRINT = true;  // TRUE if sizing is reported to output (eio)
    1347        1983 :     int NumFansSized = 0; // counter used to deallocate temporary string array after all fans have been sized
    1348             : 
    1349        1983 :     std::string SizingString = sizingPrefix + " [m3/s]";
    1350             : 
    1351        1983 :     Real64 _tempFlow = maxAirFlowRate; // autosized flow rate of fan [m3/s]
    1352        1983 :     state.dataSize->DataAutosizable = maxAirFlowRateIsAutosized;
    1353        1983 :     state.dataSize->DataEMSOverrideON = EMSMaxAirFlowRateOverrideOn;
    1354        1983 :     state.dataSize->DataEMSOverride = EMSMaxAirFlowRateValue;
    1355        1983 :     airLoopNum = state.dataSize->CurSysNum;
    1356             : 
    1357        1983 :     bool errorsFound = false;
    1358        1983 :     SystemAirFlowSizer sizerSystemAirFlow;
    1359        1983 :     sizerSystemAirFlow.overrideSizingString(SizingString);
    1360        1983 :     sizerSystemAirFlow.initializeWithinEP(state, HVAC::fanTypeNames[(int)type], Name, _bPRINT, routineName);
    1361        1983 :     maxAirFlowRate = sizerSystemAirFlow.size(state, _tempFlow, errorsFound);
    1362             : 
    1363        1983 :     state.dataSize->DataAutosizable = true;
    1364        1983 :     state.dataSize->DataEMSOverrideON = false;
    1365        1983 :     state.dataSize->DataEMSOverride = 0.0;
    1366             : 
    1367        1983 :     Real64 _volFlow = maxAirFlowRate; // Maximum volumetric airflow through fan [m3/s at standard conditions]
    1368        1983 :     if (type == HVAC::FanType::ComponentModel) {
    1369             :         // Get air density at standard conditions and get mass airflow through fan
    1370             :         // From WeatherManager:
    1371             :         //   StdBaroPress=(101.325d0*(1.0d0-2.25577d-05*WeatherFileElevation)**5.2559d0)*1000.d0
    1372             :         //   StdRhoAir=PsyRhoAirFnPbTdbW(StdBaroPress,20,0)
    1373             :         // From PsychRoutines:
    1374             :         //   w=MAX(dw,1.0d-5)
    1375             :         //   rhoair = pb/(287.d0*(tdb+Constant::Kelvin())*(1.0d0+1.6077687d0*w))
    1376           5 :         Real64 _rhoAir = state.dataEnvrn->StdRhoAir;
    1377             : 
    1378             :         // Adjust max fan volumetric airflow using fan sizing factor
    1379           5 :         _volFlow *= sizingFactor; //[m3/s at standard conditions]
    1380             : 
    1381             :         // Calculate max fan static pressure rise using max fan volumetric flow, std air density, air-handling system characteristics,
    1382             :         //   and Sherman-Wray system curve model (assumes static pressure surrounding air distribution system is zero)
    1383           5 :         Real64 _ductStaticPress = Curve::CurveValue(state, pressResetCurveNum, _volFlow);                // Duct static pressure setpoint [Pa]
    1384           5 :         Real64 _deltaPressTot = Curve::CurveValue(state, pressRiseCurveNum, _volFlow, _ductStaticPress); // Max fan total pressure rise [Pa]
    1385           5 :         Real64 _outletVelPress = 0.5 * _rhoAir * pow_2(_volFlow / outletArea);                           // Max fan outlet velocity pressure [Pa]
    1386             :         // Outlet velocity pressure cannot exceed total pressure rise
    1387           5 :         _outletVelPress = min(_outletVelPress, _deltaPressTot);
    1388           5 :         deltaPress = _deltaPressTot - _outletVelPress; // Max fan static pressure rise [Pa]
    1389             : 
    1390             :         // Calculate max fan air power using volumetric flow abd corresponding fan static pressure rise
    1391           5 :         airPower = _volFlow * deltaPress; //[W]
    1392             : 
    1393             :         // Calculate fan wheel efficiency at max fan volumetric flow and corresponding fan static pressure rise,
    1394             :         //   using fan characteristics and Wray dimensionless fan static efficiency model
    1395           5 :         Real64 _eulerNum = (deltaPress * pow_4(wheelDia)) / (_rhoAir * pow_2(_volFlow)); //[-]
    1396           5 :         Real64 _normalizedEulerNum = std::log10(_eulerNum / eulerMaxEff);
    1397           5 :         wheelEff = (_normalizedEulerNum <= 0.0) ? Curve::CurveValue(state, plTotalEffNormCurveNum, _normalizedEulerNum)
    1398           1 :                                                 : Curve::CurveValue(state, plTotalEffStallCurveNum, _normalizedEulerNum);
    1399             : 
    1400           5 :         wheelEff = max(wheelEff * maxEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
    1401             : 
    1402             :         // Calculate max fan shaft power using fan air power and fan efficiency
    1403             :         // at max fan static pressure rise and max fan volumetric flow
    1404           5 :         shaftPower = (airPower / wheelEff); //[W]
    1405           5 :         shaftPowerMax = shaftPower;         //[W]
    1406             : 
    1407             :         // Calculate fan shaft speed, motor speed, and fan torque using Wray dimensionless fan airflow model
    1408           5 :         Real64 _dimFlow = (_normalizedEulerNum <= 0.0) ? // Fan dimensionless airflow [-]
    1409           4 :                               Curve::CurveValue(state, dimFlowNormCurveNum, _normalizedEulerNum)
    1410           1 :                                                        : Curve::CurveValue(state, dimFlowStallCurveNum, _normalizedEulerNum); //[-]
    1411             : 
    1412           5 :         Real64 _speedRadS = _volFlow / (_dimFlow * maxDimFlow * pow_3(wheelDia)); //[rad/s]
    1413           5 :         fanSpeed = _speedRadS * 9.549296586;                                      //[rpm, conversion factor is 30/PI]
    1414             : 
    1415           5 :         if (pulleyDiaRatio == DataSizing::AutoSize) {
    1416             :             // WRITE(*,*) 'Autosizing pulley drive ratio'
    1417           5 :             pulleyDiaRatio = fanSpeed / motorMaxSpeed; //[-]
    1418             :         }
    1419             : 
    1420             :         // For direct-drive, should have PulleyDiaRatio = 1
    1421           5 :         Real64 _motorSpeed = fanSpeed / pulleyDiaRatio; //[rpm]
    1422             : 
    1423             :         // Check for inconsistent drive ratio and motor speed, and report design fan speed with warning
    1424           5 :         if (_motorSpeed > (motorMaxSpeed + 1.e-5)) {
    1425           0 :             ShowWarningError(state,
    1426           0 :                              format("Drive ratio for {}: {} is too low at design conditions -- check motor speed and drive ratio inputs",
    1427           0 :                                     HVAC::fanTypeNames[(int)type],
    1428           0 :                                     Name));
    1429           0 :             ShowContinueError(state, format("...Design fan speed [rev/min]: {:.2R}", fanSpeed));
    1430             :         }
    1431             : 
    1432           5 :         fanTorque = shaftPower / _speedRadS; //[N-m]
    1433             : 
    1434           5 :         if (beltMaxTorque == DataSizing::AutoSize) {
    1435             :             // WRITE(*,*) 'Autosizing fan belt'
    1436           5 :             beltMaxTorque = fanTorque; //[N-m]
    1437             :         }
    1438             :         // Adjust max belt torque using belt sizing factor
    1439           5 :         beltMaxTorque *= beltSizingFactor; //[N-m]
    1440             : 
    1441             :         // Check for undersized belt and report design size with warning
    1442           5 :         if (fanTorque > (beltMaxTorque + 1.e-5)) {
    1443           0 :             ShowWarningError(state,
    1444           0 :                              format("Belt for {}: {} is undersized at design conditions -- check belt inputs", HVAC::fanTypeNames[(int)type], Name));
    1445           0 :             ShowContinueError(state, format("...Design belt output torque (without oversizing) [Nm]: {:.2R}", fanTorque));
    1446             :         }
    1447             : 
    1448             :         // Calculate belt max efficiency using correlations and coefficients based on AMCA data
    1449             :         // Direct-drive is represented using curve coefficients such that "belt" max eff and PL eff = 1.0
    1450           5 :         Real64 _XbeltMax = std::log(shaftPowerMax / 746.0); // Natural log of belt output power in hp
    1451           5 :         beltMaxEff = (beltMaxEffCurveNum != 0) ? std::exp(Curve::CurveValue(state, beltMaxEffCurveNum, _XbeltMax)) : 1.0;
    1452             : 
    1453             :         // Calculate belt part-load drive efficiency and input power using correlations and coefficients based on ACEEE data
    1454           5 :         Real64 _torqueRatio = fanTorque / beltMaxTorque; //[-]
    1455             :         Real64 _plBeltEff;                               // Belt normalized (part-load) efficiency [-]
    1456           5 :         if ((_torqueRatio <= beltTorqueTrans) && (plBeltEffReg1CurveNum != 0)) {
    1457           0 :             _plBeltEff = Curve::CurveValue(state, plBeltEffReg1CurveNum, _torqueRatio); //[-]
    1458           5 :         } else if ((_torqueRatio > beltTorqueTrans) && (_torqueRatio <= 1.0) && (plBeltEffReg2CurveNum != 0)) {
    1459           5 :             _plBeltEff = Curve::CurveValue(state, plBeltEffReg2CurveNum, _torqueRatio); //[-]
    1460           0 :         } else if ((_torqueRatio > 1.0) && (plBeltEffReg3CurveNum != 0)) {
    1461           0 :             _plBeltEff = Curve::CurveValue(state, plBeltEffReg3CurveNum, _torqueRatio); //[-]
    1462             :         } else {
    1463           0 :             _plBeltEff = 1.0; // Direct drive or no curve specified - use constant efficiency
    1464             :         }
    1465           5 :         beltEff = beltMaxEff * _plBeltEff;     //[-]
    1466           5 :         beltEff = max(beltEff, 0.01);          // Minimum efficiency is 1% to avoid numerical errors
    1467           5 :         beltInputPower = shaftPower / beltEff; //[W]
    1468             : 
    1469           5 :         if (motorMaxOutPower == DataSizing::AutoSize) {
    1470             :             // WRITE(*,*) 'Autosizing fan motor'
    1471           5 :             motorMaxOutPower = beltInputPower;
    1472             :         }
    1473             :         // Adjust max motor output power using motor sizing factor
    1474           5 :         motorMaxOutPower *= motorSizingFactor; //[W]
    1475             : 
    1476             :         // Check for undersized motor and report design size with warning
    1477           5 :         if (beltInputPower > (motorMaxOutPower + 1.e-5)) {
    1478           0 :             ShowWarningError(
    1479           0 :                 state, format("Motor for {}: {} is undersized at design conditions -- check motor inputs", HVAC::fanTypeNames[(int)type], Name));
    1480           0 :             ShowContinueError(state, format("...Design motor output power (without oversizing) [W]: {:.2R}", beltInputPower));
    1481             :         }
    1482             : 
    1483             :         // Calculate motor max efficiency using correlations and coefficients based on MotorMaster+ data
    1484           5 :         Real64 _XmotorMax = std::log(motorMaxOutPower / 746.0); // Natural log of motor output power in hp
    1485           5 :         motorMaxEff = (motorMaxEffCurveNum != 0) ? Curve::CurveValue(state, motorMaxEffCurveNum, _XmotorMax) : 1.0;
    1486             : 
    1487             :         // Calculate motor part-load efficiency and input power using correlations and coefficients based on MotorMaster+ data
    1488           5 :         Real64 _motorOutPowerRatio = beltInputPower / motorMaxOutPower; //[-]
    1489             :         // Motor normalized (part-load) efficiency [-]
    1490           5 :         Real64 _plMotorEff = (plMotorEffCurveNum != 0) ? Curve::CurveValue(state, plMotorEffCurveNum, _motorOutPowerRatio) : 1.0;
    1491             : 
    1492           5 :         motorEff = max(motorMaxEff * _plMotorEff, 0.01);
    1493             : 
    1494             :         // Calculate motor input power using belt input power and motor efficiency
    1495           5 :         motorInputPower = beltInputPower / motorEff; //[W]
    1496             : 
    1497             :         // Calculate max VFD efficiency and input power using correlations and coefficients based on VFD type
    1498           5 :         if ((vfdEffType == VFDEffType::Speed) && (vfdEffCurveNum != 0)) {
    1499           0 :             Real64 _vfdSpeedRatio = _motorSpeed / motorMaxSpeed; // Ratio of motor speed to motor max speed [-]
    1500           0 :             vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdSpeedRatio);
    1501           5 :         } else if ((vfdEffType == VFDEffType::Power) && (vfdEffCurveNum != 0)) {
    1502           5 :             if (vfdMaxOutPower == DataSizing::AutoSize) {
    1503             :                 // WRITE(*,*) 'Autosizing fan VFD'
    1504           5 :                 vfdMaxOutPower = motorInputPower;
    1505             :             }
    1506             :             // Adjust max VFD output power using VFD sizing factor
    1507           5 :             vfdMaxOutPower *= vfdSizingFactor; //[W]
    1508             : 
    1509             :             // Check for undersized VFD and report design size with warning
    1510           5 :             if (motorInputPower > (vfdMaxOutPower + 1.e-5)) {
    1511           0 :                 ShowWarningError(
    1512           0 :                     state, format("VFD for {}: {} is undersized at design conditions -- check VFD inputs", HVAC::fanTypeNames[(int)type], Name));
    1513           0 :                 ShowContinueError(state, format("...Design VFD output power (without oversizing) [W]: {:.2R}", motorInputPower));
    1514             :             }
    1515             : 
    1516           5 :             Real64 _vfdOutPowerRatio = motorInputPower / vfdMaxOutPower; // Ratio of VFD output power to max VFD output power [-]
    1517           5 :             vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdOutPowerRatio);
    1518           5 :         } else {
    1519             :             // No curve specified - use constant efficiency
    1520           0 :             vfdMaxOutPower = 0.0;
    1521           0 :             vfdEff = 0.97;
    1522             :         }
    1523             : 
    1524           5 :         vfdEff = max(vfdEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
    1525             : 
    1526             :         // Calculate VFD "rated" input power using motor input power and VFD efficiency
    1527           5 :         Real64 _ratedPower = motorInputPower / vfdEff; //[W]
    1528             : 
    1529             :         // Calculate combined fan system efficiency: includes fan, belt, motor, and VFD
    1530             :         // Equivalent to fan%FanAirPower / fan%FanPower
    1531           5 :         totalEff = wheelEff * beltEff * motorEff * vfdEff;
    1532             : 
    1533             :         // Report fan, belt, motor, and VFD characteristics at design condition to .eio file
    1534           5 :         std::string_view fanTypeName = HVAC::fanTypeNames[(int)type];
    1535             : 
    1536           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Airflow [m3/s]", _volFlow);
    1537           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Static Pressure Rise [Pa]", deltaPress);
    1538           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Shaft Power [W]", shaftPower);
    1539           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Motor Output Power [W]", motorMaxOutPower);
    1540           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design VFD Output Power [W]", vfdMaxOutPower);
    1541           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Rated Power [W]", _ratedPower);
    1542           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Drive Ratio []", pulleyDiaRatio);
    1543           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Belt Output Torque [Nm]", beltMaxTorque);
    1544           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Fan Efficiency  []", wheelEff);
    1545           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Maximum Belt Efficiency []", beltMaxEff);
    1546           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Belt Efficiency []", beltEff);
    1547           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Maximum Motor Efficiency []", motorMaxEff);
    1548           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Motor Efficiency []", motorEff);
    1549           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design VFD Efficiency []", vfdEff);
    1550           5 :         BaseSizer::reportSizerOutput(state, fanTypeName, Name, "Design Combined Efficiency []", totalEff);
    1551             :     } // End fan component sizing
    1552             : 
    1553             :     // Rearrange order to match table and use FanVolFlow to calculate RatedPower
    1554             :     // ALSO generates values if Component Model fan, for which DeltaPress and FanEff vary with flow
    1555        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, Name, HVAC::fanTypeNames[(int)type]);
    1556        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, Name, totalEff);
    1557        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, Name, deltaPress);
    1558        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, Name, _volFlow);
    1559        1983 :     Real64 _ratedPower = _volFlow * deltaPress / totalEff; // total fan power
    1560        1983 :     BaseSizer::reportSizerOutput(state, HVAC::fanTypeNames[(int)type], Name, "Design Electric Power Consumption [W]", _ratedPower);
    1561        1983 :     if (type != HVAC::FanType::ComponentModel) {
    1562        1978 :         designPointFEI = FanSystem::report_fei(state, _volFlow, _ratedPower, deltaPress);
    1563             :     }
    1564        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, Name, _ratedPower);
    1565        1983 :     if (_volFlow != 0.0) {
    1566        1974 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwrPerFlow, Name, _ratedPower / _volFlow);
    1567             :     }
    1568        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, Name, motorInAirFrac);
    1569        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, Name, endUseSubcategoryName);
    1570        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, Name, designPointFEI);
    1571             : 
    1572             :     // Std 229 Fans (Fans.cc)
    1573        3966 :     OutputReportPredefined::PreDefTableEntry(
    1574        1983 :         state, state.dataOutRptPredefined->pdchFanPurpose, Name, "N/A"); // fan.FanType); // purpose? not the same
    1575        3966 :     OutputReportPredefined::PreDefTableEntry(state,
    1576        1983 :                                              state.dataOutRptPredefined->pdchFanAutosized,
    1577             :                                              Name,
    1578        1983 :                                              maxAirFlowRateIsAutosized ? "Yes" : "No"); // autosizable vs. autosized equivalent?
    1579        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorEff, Name, motorEff);
    1580        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatToZoneFrac, Name, 0.0);
    1581        1983 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatZone, Name, "N/A");
    1582        1983 :     if (airLoopNum == 0) {
    1583         819 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, "N/A");
    1584        1164 :     } else if (airLoopNum <= state.dataHVACGlobal->NumPrimaryAirSys) {
    1585        2328 :         OutputReportPredefined::PreDefTableEntry(
    1586        2328 :             state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name);
    1587             :     } else {
    1588           0 :         OutputReportPredefined::PreDefTableEntry(
    1589             :             state,
    1590           0 :             state.dataOutRptPredefined->pdchFanAirLoopName,
    1591             :             Name,
    1592           0 :             state.dataAirLoopHVACDOAS->airloopDOAS[airLoopNum - state.dataHVACGlobal->NumPrimaryAirSys - 1].Name);
    1593             :     }
    1594             : 
    1595        1983 :     if (nightVentPerfNum > 0) {
    1596           0 :         if (state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirFlowRate == DataSizing::AutoSize) {
    1597           0 :             state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirFlowRate = maxAirFlowRate;
    1598             :         }
    1599             :     }
    1600             : 
    1601             :     // Now that sizing is done, do check if the design point of fan is covered in the fault Fan Curve
    1602        1983 :     if (faultyFilterFlag) {
    1603           1 :         auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
    1604             : 
    1605             :         // Check fault availability schedules
    1606           1 :         if (!fault.CheckFaultyAirFilterFanCurve(state)) {
    1607           0 :             ShowSevereError(state, format("FaultModel:Fouling:AirFilter = \"{}\"", fault.Name));
    1608           0 :             ShowContinueError(state,
    1609           0 :                               format("Invalid Fan Curve Name = \"{}\" does not cover ", state.dataCurveManager->PerfCurve(fault.fanCurveNum)->Name));
    1610           0 :             ShowContinueError(state, format("the operational point of Fan {}", Name));
    1611           0 :             ShowFatalError(state, format("SizeFan: Invalid FaultModel:Fouling:AirFilter={}", fault.Name));
    1612             :         }
    1613             :     }
    1614        1983 : } // FanComponent::set_size()
    1615             : 
    1616    12106020 : void FanComponent::simulateConstant(EnergyPlusData &state)
    1617             : {
    1618             : 
    1619             :     // SUBROUTINE INFORMATION:
    1620             :     //       AUTHOR         Unknown
    1621             :     //       DATE WRITTEN   Unknown
    1622             :     //       MODIFIED       Brent Griffith, May 2009, added EMS override
    1623             :     //                      Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
    1624             : 
    1625             :     // PURPOSE OF THIS SUBROUTINE:
    1626             :     // This subroutine simulates the simple constant volume fan.
    1627             : 
    1628             :     // METHODOLOGY EMPLOYED:
    1629             :     // Converts design pressure rise and efficiency into fan power and temperature rise
    1630             :     // Constant fan pressure rise is assumed.
    1631             : 
    1632             :     // REFERENCES:
    1633             :     // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
    1634             : 
    1635             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1636             :     Real64 _deltaPress; // [N/m2]
    1637             :     Real64 _totalEff;
    1638             :     Real64 _motorInAirFrac;
    1639             :     Real64 _motorEff;
    1640             :     Real64 _shaftPower; // power delivered to fan shaft
    1641             : 
    1642    12106020 :     if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
    1643           0 :         auto const &nightVentPerf = state.dataFans->NightVentPerf(nightVentPerfNum);
    1644           0 :         _deltaPress = nightVentPerf.DeltaPress;
    1645           0 :         _totalEff = nightVentPerf.FanEff;
    1646           0 :         _motorEff = nightVentPerf.MotEff;
    1647           0 :         _motorInAirFrac = nightVentPerf.MotInAirFrac;
    1648             :     } else {
    1649    12106020 :         _deltaPress = deltaPress;
    1650    12106020 :         _totalEff = totalEff;
    1651    12106020 :         _motorEff = motorEff;
    1652    12106020 :         _motorInAirFrac = motorInAirFrac;
    1653             :     }
    1654             : 
    1655             :     // For a Constant Volume Simple Fan the Max Flow Rate is the Flow Rate for the fan
    1656    12106020 :     Real64 _rhoAir = rhoAirStdInit;
    1657    12106020 :     Real64 _massFlow = inletAirMassFlowRate;
    1658             : 
    1659             :     // Faulty fan operations
    1660             :     // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
    1661    12106020 :     if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation)) {
    1662           0 :         auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
    1663             :         // Check fault availability schedules
    1664           0 :         if (ScheduleManager::GetCurrentScheduleValue(state, fault.availSchedNum) > 0.0) {
    1665             :             // Decrease of the Fan Design Volume Flow Rate [m3/sec]
    1666             :             Real64 _fanDesignFlowRateDec =
    1667           0 :                 CalFaultyFanAirFlowReduction(state,
    1668           0 :                                              Name,
    1669             :                                              maxAirFlowRate,
    1670             :                                              deltaPress,
    1671           0 :                                              (ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) - 1) * deltaPress,
    1672             :                                              fault.fanCurveNum);
    1673             : 
    1674             :             // Update MassFlow & DeltaPress of the fan
    1675           0 :             _massFlow = min(_massFlow, maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir);
    1676           0 :             _deltaPress = ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) * deltaPress;
    1677             :         }
    1678             :     }
    1679             : 
    1680             :     // EMS overwrite MassFlow, DeltaPress, and FanEff
    1681    12106020 :     if (EMSMaxMassFlowOverrideOn) _massFlow = EMSAirMassFlowValue;
    1682    12106020 :     if (EMSPressureOverrideOn) _deltaPress = EMSPressureValue;
    1683    12106020 :     if (EMSTotalEffOverrideOn) _totalEff = EMSTotalEffValue;
    1684             : 
    1685    12106020 :     _massFlow = min(_massFlow, maxAirMassFlowRate);
    1686    12106020 :     _massFlow = max(_massFlow, minAirMassFlowRate);
    1687             : 
    1688             :     // Determine the Fan Schedule for the Time step
    1689    14198735 :     if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 || state.dataHVACGlobal->TurnFansOn) &&
    1690    14198735 :         !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
    1691             :         // Fan is operating
    1692    10122463 :         totalPower = max(0.0, _massFlow * _deltaPress / (_totalEff * _rhoAir)); // total fan power
    1693    10122463 :         _shaftPower = _motorEff * totalPower;                                   // power delivered to shaft
    1694    10122463 :         powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
    1695    10122463 :         outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
    1696             :         // This fan does not change the moisture or Mass Flow across the component
    1697    10122463 :         outletAirHumRat = inletAirHumRat;
    1698    10122463 :         outletAirMassFlowRate = _massFlow;
    1699    10122463 :         outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    1700             : 
    1701             :     } else {
    1702             :         // Fan is off and not operating no power consumed and mass flow rate.
    1703     1983557 :         totalPower = 0.0;
    1704     1983557 :         powerLossToAir = 0.0;
    1705     1983557 :         outletAirMassFlowRate = 0.0;
    1706     1983557 :         outletAirHumRat = inletAirHumRat;
    1707     1983557 :         outletAirEnthalpy = inletAirEnthalpy;
    1708     1983557 :         outletAirTemp = inletAirTemp;
    1709             :         // Set the Control Flow variables to 0.0 flow when OFF.
    1710     1983557 :         massFlowRateMaxAvail = 0.0;
    1711     1983557 :         massFlowRateMinAvail = 0.0;
    1712             :     }
    1713    12106020 : } // FanComponent::simulateConstant
    1714             : 
    1715    24416547 : void FanComponent::simulateVAV(EnergyPlusData &state, ObjexxFCL::Optional<Real64 const> _pressureRise)
    1716             : {
    1717             : 
    1718             :     // SUBROUTINE INFORMATION:
    1719             :     //       AUTHOR         Unknown
    1720             :     //       DATE WRITTEN   Unknown
    1721             :     //       MODIFIED       Phil Haves
    1722             :     //                      Brent Griffith, May 2009 for EMS
    1723             :     //                      Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
    1724             : 
    1725             :     // PURPOSE OF THIS SUBROUTINE:
    1726             :     // This subroutine simulates the simple variable volume fan.
    1727             : 
    1728             :     // METHODOLOGY EMPLOYED:
    1729             :     // Converts design pressure rise and efficiency into fan power and temperature rise
    1730             :     // Constant fan pressure rise is assumed.
    1731             :     // Uses curves of fan power fraction vs. fan part load to determine fan power at
    1732             :     // off design conditions.
    1733             : 
    1734             :     // REFERENCES:
    1735             :     // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
    1736             : 
    1737             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1738             :     Real64 _deltaPress; // [N/m2 = Pa]
    1739             :     Real64 _totalEff;   // Total fan efficiency - combined efficiency of fan, drive train,
    1740             :     Real64 _maxAirMassFlowRate;
    1741             :     Real64 _motorInAirFrac;
    1742             :     Real64 _motorEff;
    1743             :     Real64 _partLoadFrac;
    1744             : 
    1745             :     // Simple Variable Volume Fan - default values from DOE-2
    1746             :     // Type of Fan          Coeff1       Coeff2       Coeff3        Coeff4      Coeff5
    1747             :     // INLET VANE DAMPERS   0.35071223   0.30850535   -0.54137364   0.87198823  0.000
    1748             :     // DISCHARGE DAMPERS    0.37073425   0.97250253   -0.34240761   0.000       0.000
    1749             :     // VARIABLE SPEED MOTOR 0.0015302446 0.0052080574  1.1086242   -0.11635563  0.000
    1750             : 
    1751    24416547 :     if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
    1752           0 :         auto const &nightVentPerf = state.dataFans->NightVentPerf(nightVentPerfNum);
    1753           0 :         _deltaPress = nightVentPerf.DeltaPress;
    1754           0 :         _totalEff = nightVentPerf.FanEff;
    1755           0 :         _motorEff = nightVentPerf.MotEff;
    1756           0 :         _motorInAirFrac = nightVentPerf.MotInAirFrac;
    1757           0 :         _maxAirMassFlowRate = nightVentPerf.MaxAirMassFlowRate;
    1758             :     } else {
    1759    24416547 :         if (present(_pressureRise)) {
    1760       28609 :             _deltaPress = _pressureRise;
    1761             :         } else {
    1762    24387938 :             _deltaPress = deltaPress;
    1763             :         }
    1764    24416547 :         _totalEff = totalEff;
    1765    24416547 :         _motorEff = motorEff;
    1766    24416547 :         _motorInAirFrac = motorInAirFrac;
    1767    24416547 :         _maxAirMassFlowRate = maxAirMassFlowRate;
    1768             :     }
    1769             : 
    1770    24416547 :     Real64 _rhoAir = rhoAirStdInit;
    1771    24416547 :     Real64 _massFlow = inletAirMassFlowRate;
    1772    24416547 :     Real64 _maxAirFlowRate = maxAirFlowRate;
    1773             : 
    1774             :     // Faulty fan operations
    1775             :     // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
    1776    24419115 :     if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation) &&
    1777        2568 :         (!EMSMaxMassFlowOverrideOn)) {
    1778             : 
    1779        2568 :         auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
    1780             :         // Check fault availability schedules
    1781        2568 :         if (ScheduleManager::GetCurrentScheduleValue(state, fault.availSchedNum) > 0.0) {
    1782             :             Real64 _fanDesignFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
    1783        7704 :                 CalFaultyFanAirFlowReduction(state,
    1784        2568 :                                              Name,
    1785             :                                              maxAirFlowRate,
    1786             :                                              deltaPress,
    1787        2568 :                                              (ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) - 1) * deltaPress,
    1788             :                                              fault.fanCurveNum);
    1789             : 
    1790             :             // Update MassFlow & DeltaPress of the fan
    1791        2568 :             _maxAirFlowRate = maxAirFlowRate - _fanDesignFlowRateDec;
    1792        2568 :             _maxAirMassFlowRate = maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir;
    1793        2568 :             _deltaPress = ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) * deltaPress;
    1794             :         }
    1795             :     }
    1796             : 
    1797             :     // EMS overwrite MassFlow, DeltaPress, and FanEff
    1798    24416547 :     if (EMSPressureOverrideOn) _deltaPress = EMSPressureValue;
    1799    24416547 :     if (EMSTotalEffOverrideOn) _totalEff = EMSTotalEffValue;
    1800    24416547 :     if (EMSMaxMassFlowOverrideOn) _massFlow = EMSAirMassFlowValue;
    1801             : 
    1802    24416547 :     _massFlow = min(_massFlow, _maxAirMassFlowRate);
    1803             : 
    1804             :     // Determine the Fan Schedule for the Time step
    1805    28256110 :     if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 || state.dataHVACGlobal->TurnFansOn) &&
    1806    28256110 :         !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
    1807             :         // Fan is operating - calculate power loss and enthalpy rise
    1808             :         //  fan%FanPower = PartLoadFrac*FullMassFlow*DeltaPress/(FanEff*RhoAir) ! total fan power
    1809             :         // Calculate and check limits on fraction of system flow
    1810             :         // unused0909    MaxFlowFrac = 1.0
    1811             :         // MinFlowFrac is calculated from the ration of the volume flows and is non-dimensional
    1812    22356872 :         Real64 _minFlowFrac = minAirFlowRate / _maxAirFlowRate;
    1813             :         // The actual flow fraction is calculated from MassFlow and the MaxVolumeFlow * AirDensity
    1814    22356872 :         Real64 _flowFracActual = _massFlow / _maxAirMassFlowRate;
    1815             : 
    1816             :         // Calculate the part Load Fraction             (PH 7/13/03)
    1817             : 
    1818    22356872 :         Real64 _flowFracForPower = max(_minFlowFrac, min(_flowFracActual, 1.0)); // limit flow fraction to allowed range
    1819    22356872 :         if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
    1820           0 :             _partLoadFrac = 1.0;
    1821             :         } else {
    1822    22356872 :             _partLoadFrac = coeffs[0] + coeffs[1] * _flowFracForPower + coeffs[2] * pow_2(_flowFracForPower) + coeffs[3] * pow_3(_flowFracForPower) +
    1823    22356872 :                             coeffs[4] * pow_4(_flowFracForPower);
    1824             :         }
    1825             : 
    1826    22356872 :         totalPower = max(0.0, _partLoadFrac * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir)); // total fan power (PH 7/13/03)
    1827             : 
    1828    22356872 :         Real64 _shaftPower = _motorEff * totalPower; // power delivered to shaft
    1829    22356872 :         powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
    1830    22356872 :         outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
    1831             :         // This fan does not change the moisture or Mass Flow across the component
    1832    22356872 :         outletAirHumRat = inletAirHumRat;
    1833    22356872 :         outletAirMassFlowRate = _massFlow;
    1834    22356872 :         outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    1835             : 
    1836             :         // KHL/FB, 2/10/2011. NFP implemented as CR 8338.
    1837             :         // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
    1838             :         //  avoid the unrealistic high temperature rise across the fan.
    1839             :         // TH, 2/15/2011
    1840             :         // This change caused diffs for VAV systems when fan runs at less than 10% flow conditions.
    1841             :         //  A potential way to improve is to check the temperature rise across the fan first,
    1842             :         //  if it is too high (say > 20C) then applies the code.
    1843    22356872 :         Real64 _deltaTAcrossFan = outletAirTemp - inletAirTemp;
    1844    22356872 :         if (_deltaTAcrossFan > 20.0) {
    1845             :             // added to address the fan heat issue during low air flow conditions
    1846             :             Real64 _fanPoweratLowMinimum; // Fan Power at Low Minimum Airflow [W]
    1847             :             Real64 _partLoadFracatLowMin;
    1848           0 :             Real64 _minFlowFracLimitFanHeat = 0.10;
    1849           0 :             if (_flowFracForPower < _minFlowFracLimitFanHeat) {
    1850           0 :                 _partLoadFracatLowMin = coeffs[0] + coeffs[1] * _minFlowFracLimitFanHeat + coeffs[2] * pow_2(_minFlowFracLimitFanHeat) +
    1851           0 :                                         coeffs[3] * pow_3(_minFlowFracLimitFanHeat) + coeffs[4] * pow_4(_minFlowFracLimitFanHeat);
    1852           0 :                 _fanPoweratLowMinimum = _partLoadFracatLowMin * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir);
    1853           0 :                 totalPower = max(0.0, _flowFracForPower * _fanPoweratLowMinimum / _minFlowFracLimitFanHeat);
    1854           0 :             } else if (_flowFracActual < _minFlowFracLimitFanHeat) {
    1855           0 :                 _partLoadFracatLowMin = coeffs[0] + coeffs[1] * _minFlowFracLimitFanHeat + coeffs[2] * pow_2(_minFlowFracLimitFanHeat) +
    1856           0 :                                         coeffs[3] * pow_3(_minFlowFracLimitFanHeat) + coeffs[4] * pow_4(_minFlowFracLimitFanHeat);
    1857           0 :                 _fanPoweratLowMinimum = _partLoadFracatLowMin * _maxAirMassFlowRate * _deltaPress / (_totalEff * _rhoAir);
    1858           0 :                 totalPower = max(0.0, _flowFracActual * _fanPoweratLowMinimum / _minFlowFracLimitFanHeat);
    1859             :             }
    1860           0 :             _shaftPower = _motorEff * totalPower; // power delivered to shaft
    1861           0 :             powerLossToAir = _shaftPower + (totalPower - _shaftPower) * _motorInAirFrac;
    1862           0 :             outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
    1863             :             // This fan does not change the moisture or Mass Flow across the component
    1864           0 :             outletAirHumRat = inletAirHumRat;
    1865           0 :             outletAirMassFlowRate = _massFlow;
    1866           0 :             outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    1867             :         }
    1868             : 
    1869             :     } else {
    1870             :         // Fan is off and not operating no power consumed and mass flow rate.
    1871     2059675 :         totalPower = 0.0;
    1872     2059675 :         powerLossToAir = 0.0;
    1873     2059675 :         outletAirMassFlowRate = 0.0;
    1874     2059675 :         outletAirHumRat = inletAirHumRat;
    1875     2059675 :         outletAirEnthalpy = inletAirEnthalpy;
    1876     2059675 :         outletAirTemp = inletAirTemp;
    1877             :         // Set the Control Flow variables to 0.0 flow when OFF.
    1878     2059675 :         massFlowRateMaxAvail = 0.0;
    1879     2059675 :         massFlowRateMinAvail = 0.0;
    1880             :     }
    1881    24416547 : } // FanComponent::SimVAV()
    1882             : 
    1883   107240192 : void FanComponent::simulateOnOff(EnergyPlusData &state, ObjexxFCL::Optional<Real64 const> _speedRatio)
    1884             : {
    1885             : 
    1886             :     // SUBROUTINE INFORMATION:
    1887             :     //       AUTHOR         Unknown
    1888             :     //       DATE WRITTEN   Unknown
    1889             :     //       MODIFIED       Shirey, May 2001
    1890             :     //                      R. Raustad - FSEC, Jan 2009 - added SpeedRatio for multi-speed fans
    1891             :     //                      Brent Griffith, May 2009 for EMS
    1892             :     //                      Rongpeng Zhang, April 2015, added faulty fan operations due to fouling air filters
    1893             : 
    1894             :     // PURPOSE OF THIS SUBROUTINE:
    1895             :     // This subroutine simulates the simple on/off fan.
    1896             : 
    1897             :     // METHODOLOGY EMPLOYED:
    1898             :     // Converts design pressure rise and efficiency into fan power and temperature rise
    1899             :     // Constant fan pressure rise is assumed.
    1900             :     // Uses curves of fan power fraction vs. fan part load to determine fan power at
    1901             :     // off design conditions.
    1902             :     // Same as simple (constant volume) fan, except added part-load curve input
    1903             : 
    1904             :     // REFERENCES:
    1905             :     // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
    1906             : 
    1907             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1908             :     Real64 _effRatioAtSpeedRatio; // Efficiency ratio at current speed ratio (Curve object)
    1909             : 
    1910   107240192 :     Real64 _massFlow = inletAirMassFlowRate;
    1911   107240192 :     Real64 _maxAirMassFlowRate = maxAirMassFlowRate;
    1912   107240192 :     Real64 _deltaPress = deltaPress; // [N/m2]
    1913   107240192 :     Real64 _totalEff = totalEff;
    1914   107240192 :     Real64 _rhoAir = rhoAirStdInit;
    1915             : 
    1916             :     // Faulty fan operations
    1917             :     // Update MassFlow & DeltaPress if there are fouling air filters corresponding to the fan
    1918   107240192 :     if (faultyFilterFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) && (!state.dataGlobal->KickOffSimulation) &&
    1919           0 :         (!EMSMaxMassFlowOverrideOn)) {
    1920             : 
    1921           0 :         auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
    1922             : 
    1923             :         // Check fault availability schedules
    1924           0 :         if (ScheduleManager::GetCurrentScheduleValue(state, fault.availSchedNum) > 0.0) {
    1925             :             Real64 _fanDesignFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
    1926           0 :                 CalFaultyFanAirFlowReduction(state,
    1927           0 :                                              Name,
    1928             :                                              maxAirFlowRate,
    1929             :                                              deltaPress,
    1930           0 :                                              (ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) - 1) * deltaPress,
    1931             :                                              fault.fanCurveNum);
    1932             : 
    1933             :             // Update MassFlow & DeltaPress of the fan
    1934           0 :             _maxAirMassFlowRate = maxAirMassFlowRate - _fanDesignFlowRateDec * _rhoAir;
    1935           0 :             _deltaPress = ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum) * deltaPress;
    1936             :         }
    1937             :     }
    1938             : 
    1939             :     // EMS overwrite MassFlow, DeltaPress, and FanEff
    1940   107240192 :     if (EMSMaxMassFlowOverrideOn) _massFlow = EMSAirMassFlowValue;
    1941   107240192 :     if (EMSPressureOverrideOn) _deltaPress = EMSPressureValue;
    1942   107240192 :     if (EMSTotalEffOverrideOn) _totalEff = EMSTotalEffValue;
    1943             : 
    1944   107240192 :     _massFlow = min(_massFlow, _maxAirMassFlowRate);
    1945   107240192 :     _massFlow = max(_massFlow, minAirMassFlowRate);
    1946   107240192 :     runtimeFrac = 0.0;
    1947             : 
    1948             :     // Determine the Fan Schedule for the Time step
    1949   109328059 :     if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 || state.dataHVACGlobal->TurnFansOn) &&
    1950   109328059 :         !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0 && maxAirMassFlowRate > 0.0) {
    1951             :         // The actual flow fraction is calculated from MassFlow and the MaxVolumeFlow * AirDensity
    1952    91424374 :         Real64 _flowFrac = _massFlow / _maxAirMassFlowRate;
    1953             : 
    1954             :         // Calculate the part load ratio, can't be greater than 1
    1955    91424374 :         Real64 _partLoadRatio = min(1.0, _flowFrac);
    1956             :         // Fan is operating
    1957    91424374 :         if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
    1958           0 :             ShowRecurringWarningErrorAtEnd(state, "Fan:OnOff, OnOffFanPartLoadFraction <= 0.0, Reset to 1.0", state.dataFans->ErrCount);
    1959           0 :             state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // avoid divide by zero or negative PLF
    1960             :         }
    1961             : 
    1962    91424374 :         if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
    1963           0 :             state.dataHVACGlobal->OnOffFanPartLoadFraction = 0.7; // a warning message is already issued from the DX coils or gas heating coil
    1964             :         }
    1965             : 
    1966             :         // Keep fan runtime fraction between 0.0 and 1.0, and RTF >= PLR
    1967    91424374 :         if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
    1968    80869769 :             runtimeFrac = _partLoadRatio;
    1969             :         } else {
    1970    10554605 :             runtimeFrac = max(0.0, min(1.0, _partLoadRatio / state.dataHVACGlobal->OnOffFanPartLoadFraction));
    1971             :         }
    1972             :         // The fan speed ratio (passed from parent) determines the fan power according to fan laws
    1973    91424374 :         if (present(_speedRatio)) {
    1974             :             //    fan%FanPower = MassFlow*DeltaPress/(FanEff*RhoAir*OnOffFanPartLoadFraction)! total fan power
    1975    89766398 :             totalPower = max(0.0, _maxAirMassFlowRate * runtimeFrac * _deltaPress / (_totalEff * _rhoAir));
    1976             : 
    1977             :             //    Do not modify fan power calculation unless fan power vs speed ratio curve is used.
    1978    89766398 :             if (powerRatioAtSpeedRatioCurveNum > 0) {
    1979             : 
    1980             :                 //      adjust RTF to be in line with speed ratio (i.e., MaxAirMassFlowRate is not MAX when SpeedRatio /= 1)
    1981             :                 //      PLR = Mdot/MAXFlow => Mdot/(MAXFlow * SpeedRatio), RTF = PLR/PLF => PLR/SpeedRatio/PLF = RTF / SpeedRatio
    1982      200468 :                 if (_speedRatio > 0.0) runtimeFrac = min(1.0, runtimeFrac / _speedRatio);
    1983             : 
    1984      200468 :                 Real64 _speedRaisedToPower = Curve::CurveValue(state, powerRatioAtSpeedRatioCurveNum, _speedRatio);
    1985      200468 :                 if (_speedRaisedToPower < 0.0) {
    1986           0 :                     if (oneTimePowerRatioCheck && !state.dataGlobal->WarmupFlag) {
    1987           0 :                         ShowSevereError(state, format("{} = {}\"", HVAC::fanTypeNames[(int)type], Name));
    1988           0 :                         ShowContinueError(state, "Error in Fan Power Ratio curve. Curve output less than 0.0.");
    1989           0 :                         ShowContinueError(state, format("Curve output = {:.5T}, fan speed ratio = {:.5T}", _speedRaisedToPower, _speedRatio));
    1990           0 :                         ShowContinueError(state, "Check curve coefficients to ensure proper power ratio as a function of fan speed ratio.");
    1991           0 :                         ShowContinueError(state, "Resetting Fan Power Ratio curve output to 0.0 and the simulation continues.");
    1992           0 :                         ShowContinueErrorTimeStamp(state, "Occurrence info:");
    1993           0 :                         oneTimePowerRatioCheck = false;
    1994             :                     }
    1995           0 :                     _speedRaisedToPower = 0.0;
    1996             :                 }
    1997      200468 :                 if (effRatioCurveNum > 0 && !state.dataGlobal->WarmupFlag) {
    1998       30475 :                     _effRatioAtSpeedRatio = Curve::CurveValue(state, effRatioCurveNum, _speedRatio);
    1999       30475 :                     if (_effRatioAtSpeedRatio < 0.01) {
    2000           0 :                         if (oneTimeEffRatioCheck && !state.dataGlobal->WarmupFlag) {
    2001           0 :                             ShowSevereError(state, format("{} = {}\"", HVAC::fanTypeNames[(int)type], Name));
    2002           0 :                             ShowContinueError(state, "Error in Fan Efficiency Ratio curve. Curve output less than 0.01.");
    2003           0 :                             ShowContinueError(state, format("Curve output = {:.5T}, fan speed ratio = {:.5T}", _effRatioAtSpeedRatio, _speedRatio));
    2004           0 :                             ShowContinueError(state, "Check curve coefficients to ensure proper efficiency ratio as a function of fan speed ratio.");
    2005           0 :                             ShowContinueError(state, "Resetting Fan Efficiency Ratio curve output to 0.01 and the simulation continues.");
    2006           0 :                             ShowContinueErrorTimeStamp(state, "Occurrence info:");
    2007           0 :                             oneTimeEffRatioCheck = false;
    2008             :                         }
    2009           0 :                         _effRatioAtSpeedRatio = 0.01;
    2010             :                     }
    2011             :                 } else {
    2012      169993 :                     _effRatioAtSpeedRatio = 1.0;
    2013             :                 }
    2014      200468 :                 totalPower *= _speedRaisedToPower / _effRatioAtSpeedRatio;
    2015             :             }
    2016             :         } else {
    2017     1657976 :             totalPower = max(0.0, _maxAirMassFlowRate * runtimeFrac * _deltaPress / (_totalEff * _rhoAir)); // total fan power
    2018             :         }
    2019             : 
    2020             :         // OnOffFanPartLoadFraction is passed via DataHVACGlobals from the cooling or heating coil that is
    2021             :         //   requesting the fan to operate in cycling fan/cycling coil mode
    2022    91424374 :         state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1 in case other on/off fan is called without a part load curve
    2023    91424374 :         Real64 _shaftPower = motorEff * totalPower;           // power delivered to shaft
    2024    91424374 :         powerLossToAir = _shaftPower + (totalPower - _shaftPower) * motorInAirFrac;
    2025    91424374 :         outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
    2026             :         // This fan does not change the moisture or Mass Flow across the component
    2027    91424374 :         outletAirHumRat = inletAirHumRat;
    2028    91424374 :         outletAirMassFlowRate = _massFlow;
    2029             :         //   fan%OutletAirTemp = Tin + PowerLossToAir/(MassFlow*PsyCpAirFnW(Win,Tin))
    2030    91424374 :         outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    2031             :     } else {
    2032             :         // Fan is off and not operating no power consumed and mass flow rate.
    2033    15815818 :         totalPower = 0.0;
    2034    15815818 :         powerLossToAir = 0.0;
    2035    15815818 :         outletAirMassFlowRate = 0.0;
    2036    15815818 :         outletAirHumRat = inletAirHumRat;
    2037    15815818 :         outletAirEnthalpy = inletAirEnthalpy;
    2038    15815818 :         outletAirTemp = inletAirTemp;
    2039             :         // Set the Control Flow variables to 0.0 flow when OFF.
    2040    15815818 :         massFlowRateMaxAvail = 0.0;
    2041    15815818 :         massFlowRateMinAvail = 0.0;
    2042             :     }
    2043   107240192 : } // FanComponent::simulateOnOff()
    2044             : 
    2045     1543429 : void FanComponent::simulateZoneExhaust(EnergyPlusData &state)
    2046             : {
    2047             : 
    2048             :     // SUBROUTINE INFORMATION:
    2049             :     //       AUTHOR         Fred Buhl
    2050             :     //       DATE WRITTEN   Jan 2000
    2051             :     //       MODIFIED       Brent Griffith, May 2009 for EMS
    2052             :     //                      Brent Griffith, Feb 2013 controls upgrade
    2053             : 
    2054             :     // PURPOSE OF THIS SUBROUTINE:
    2055             :     // This subroutine simulates the Zone Exhaust Fan
    2056             : 
    2057             :     // METHODOLOGY EMPLOYED:
    2058             :     // Converts design pressure rise and efficiency into fan power and temperature rise
    2059             :     // Constant fan pressure rise is assumed.
    2060             : 
    2061             :     // REFERENCES:
    2062             :     // ASHRAE HVAC 2 Toolkit, page 2-3 (FANSIM)
    2063             : 
    2064             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2065     1543429 :     bool _fanIsRunning = false; // There seems to be a missing else case below unless false is assumed
    2066             : 
    2067     1543429 :     Real64 _deltaPress = deltaPress; // [N/m2]
    2068     1543429 :     if (EMSPressureOverrideOn) _deltaPress = EMSPressureValue;
    2069             : 
    2070     1543429 :     Real64 _totalEff = totalEff;
    2071     1543429 :     if (EMSTotalEffOverrideOn) _totalEff = EMSTotalEffValue;
    2072             : 
    2073             :     // For a Constant Volume Simple Fan the Max Flow Rate is the Flow Rate for the fan
    2074     1543429 :     Real64 _Tin = inletAirTemp;
    2075     1543429 :     Real64 _rhoAir = rhoAirStdInit;
    2076     1543429 :     Real64 _massFlow = inletAirMassFlowRate;
    2077             : 
    2078             :     //  When the AvailManagerMode == ExhaustFanCoupledToAvailManagers then the
    2079             :     //  Exhaust Fan is  interlocked with air loop availability via global TurnFansOn and TurnFansOff variables.
    2080             :     //  There is now the option to control if user wants to decouple air loop operation and exhaust fan operation
    2081             :     //  (zone air mass balance issues).
    2082             : 
    2083             :     // apply controls to determine if operating
    2084     1543429 :     if (availManagerMode == AvailManagerMode::Coupled) {
    2085     1871204 :         if (((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0) || state.dataHVACGlobal->TurnFansOn) &&
    2086     1871204 :             !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) { // available
    2087     1197420 :             if (minTempLimitSchedNum > 0) {
    2088           0 :                 _fanIsRunning = (_Tin >= ScheduleManager::GetCurrentScheduleValue(state, minTempLimitSchedNum));
    2089             :             } else {
    2090     1197420 :                 _fanIsRunning = true;
    2091             :             }
    2092             :         } else {
    2093      242137 :             _fanIsRunning = false;
    2094             :         }
    2095             : 
    2096      103872 :     } else if (availManagerMode == AvailManagerMode::Decoupled) {
    2097      103872 :         if (ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 && _massFlow > 0.0) {
    2098       99828 :             if (minTempLimitSchedNum > 0) {
    2099           0 :                 _fanIsRunning = (_Tin >= ScheduleManager::GetCurrentScheduleValue(state, minTempLimitSchedNum));
    2100             :             } else {
    2101       99828 :                 _fanIsRunning = true;
    2102             :             }
    2103             :         } else {
    2104        4044 :             _fanIsRunning = false;
    2105             :         }
    2106             :     }
    2107             : 
    2108     1543429 :     if (_fanIsRunning) {
    2109             :         // Fan is operating
    2110     1297248 :         totalPower = max(0.0, _massFlow * _deltaPress / (_totalEff * _rhoAir)); // total fan power
    2111     1297248 :         powerLossToAir = totalPower;
    2112     1297248 :         outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / _massFlow;
    2113             :         // This fan does not change the moisture or Mass Flow across the component
    2114     1297248 :         outletAirHumRat = inletAirHumRat;
    2115     1297248 :         outletAirMassFlowRate = _massFlow;
    2116     1297248 :         outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    2117             : 
    2118             :     } else {
    2119             :         // Fan is off and not operating no power consumed and mass flow rate.
    2120      246181 :         totalPower = 0.0;
    2121      246181 :         powerLossToAir = 0.0;
    2122      246181 :         outletAirMassFlowRate = 0.0;
    2123      246181 :         outletAirHumRat = inletAirHumRat;
    2124      246181 :         outletAirEnthalpy = inletAirEnthalpy;
    2125      246181 :         outletAirTemp = inletAirTemp;
    2126             :         // Set the Control Flow variables to 0.0 flow when OFF.
    2127      246181 :         massFlowRateMaxAvail = 0.0;
    2128      246181 :         massFlowRateMinAvail = 0.0;
    2129      246181 :         inletAirMassFlowRate = 0.0;
    2130             :     }
    2131     1543429 : } // FanComponent::SimulateZoneExhaust()
    2132             : 
    2133       62668 : void FanComponent::simulateComponentModel(EnergyPlusData &state)
    2134             : {
    2135             : 
    2136             :     // SUBROUTINE INFORMATION:
    2137             :     //       AUTHOR         Craig Wray, LBNL
    2138             :     //       DATE WRITTEN   Feb 2010
    2139             : 
    2140             :     // PURPOSE OF THIS SUBROUTINE:
    2141             :     // This subroutine simulates the component model fan.
    2142             : 
    2143             :     // METHODOLOGY EMPLOYED:
    2144             :     // Calculate fan volumetric flow and corresponding fan static pressure rise,
    2145             :     //    using air-handling system characteristics and Sherman-Wray system curve model
    2146             :     // Calculate fan air power using volumetric flow and fan static pressure rise
    2147             :     // Calculate fan wheel efficiency using fan volumetric flow, fan static pressure rise,
    2148             :     //   fan characteristics, and Wray dimensionless fan static efficiency model
    2149             :     // Calculate fan shaft power using fan air power and fan static efficiency
    2150             :     // Calculate fan shaft speed and torque using Wray dimensionless fan airflow model
    2151             :     // Calculate belt part-load efficiency using correlations and coefficients based on ACEEE data
    2152             :     // Calculate belt input power using fan shaft power and belt efficiency
    2153             :     // Calculate motor part-load efficiency using correlations and coefficients based on MotorMaster+ data
    2154             :     // Calculate motor input power using belt input power and motor efficiency
    2155             :     // Calculate VFD efficiency using correlations and coefficients based on DOE data
    2156             :     // Calculate VFD input power using motor input power and VFD efficiency
    2157             :     // Calculate combined efficiency of fan, belt, motor, and VFD
    2158             :     // Calculate air temperature rise due to fan (and belt+motor if in airstream) power entering air-handler airflow
    2159             :     // Calculate output node conditions
    2160             : 
    2161             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2162             :     Real64 _maxAirMassFlowRate; // Fan Max mass airflow [kg/s]
    2163             :     Real64 _motorInAirFrac;     // Fraction of fan power input to airstream
    2164             : 
    2165             :     // Local variables
    2166             :     Real64 _dimFlow;               // Fan dimensionless airflow [-]
    2167             :     Real64 _beltPLEff;             // Belt normalized (part-load) efficiency [-]
    2168             :     Real64 _motorPLEff;            // Motor normalized (part-load) efficiency [-]
    2169       62668 :     Real64 _vfdSpeedRatio(0.0);    // Ratio of motor speed to motor max speed [-]
    2170       62668 :     Real64 _vfdOutPowerRatio(0.0); // Ratio of VFD output power to max VFD output power [-]
    2171             : 
    2172       62668 :     if (state.dataHVACGlobal->NightVentOn && nightVentPerfNum > 0) {
    2173           0 :         _motorInAirFrac = state.dataFans->NightVentPerf(nightVentPerfNum).MotInAirFrac;
    2174           0 :         _maxAirMassFlowRate = state.dataFans->NightVentPerf(nightVentPerfNum).MaxAirMassFlowRate;
    2175             :     } else {
    2176       62668 :         _motorInAirFrac = motorInAirFrac;
    2177       62668 :         _maxAirMassFlowRate = maxAirMassFlowRate;
    2178             :     }
    2179             : 
    2180             :     // Get air density at standard conditions and get mass airflow through fan
    2181             :     // From WeatherManager:
    2182             :     //   StdBaroPress=(101.325d0*(1.0d0-2.25577d-05*WeatherFileElevation)**5.2559d0)*1000.d0
    2183             :     //   StdRhoAir=PsyRhoAirFnPbTdbW(StdBaroPress,20,0)
    2184             :     // From PsychRoutines:
    2185             :     //   w=MAX(dw,1.0d-5)
    2186             :     //   rhoair = pb/(287.d0*(tdb+Constant::Kelvin())*(1.0d0+1.6077687d0*w))
    2187       62668 :     Real64 _rhoAir = rhoAirStdInit;
    2188       62668 :     Real64 _massFlow = min(inletAirMassFlowRate, maxAirMassFlowRate);
    2189             : 
    2190             :     //  IF (fan%EMSMaxMassFlowOverrideOn) MassFlow   = fan%EMSAirMassFlowValue
    2191             : 
    2192             :     // Determine the Fan Schedule for the Time step
    2193       62668 :     if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 || state.dataHVACGlobal->TurnFansOn) &&
    2194       62668 :         !state.dataHVACGlobal->TurnFansOff && _massFlow > 0.0) {
    2195             :         // Fan is operating - calculate fan pressure rise, component efficiencies and power, and also air enthalpy rise
    2196             : 
    2197             :         // Calculate fan static pressure rise using fan volumetric flow, std air density, air-handling system characteristics,
    2198             :         //   and Sherman-Wray system curve model (assumes static pressure surrounding air distribution system is zero)
    2199       48122 :         Real64 _volFlow = _massFlow / _rhoAir;                                                           //[m3/s at standard conditions]
    2200       48122 :         Real64 _ductStaticPress = Curve::CurveValue(state, pressResetCurveNum, _volFlow);                // Duct static pressure setpoint [Pa]
    2201       48122 :         Real64 _deltaPressTot = Curve::CurveValue(state, pressRiseCurveNum, _volFlow, _ductStaticPress); // Fan total pressure rise [Pa]
    2202       48122 :         Real64 _outletVelPress = 0.5 * _rhoAir * pow_2(_volFlow / outletArea);                           // Fan outlet velocity pressure [Pa]
    2203             :         // Outlet velocity pressure cannot exceed total pressure rise
    2204       48122 :         _outletVelPress = min(_outletVelPress, _deltaPressTot);
    2205       48122 :         deltaPress = _deltaPressTot - _outletVelPress; // Fan static pressure rise [Pa]
    2206             : 
    2207             :         //    IF (fan%EMSFanPressureOverrideOn) DeltaPress = fan%EMSFanPressureValue
    2208             : 
    2209             :         // Calculate fan static air power using volumetric flow and fan static pressure rise
    2210       48122 :         airPower = _volFlow * deltaPress; //[W]
    2211             : 
    2212             :         // Calculate fan wheel efficiency using fan volumetric flow, fan static pressure rise,
    2213             :         //   fan characteristics, and Wray dimensionless fan static efficiency model
    2214       48122 :         Real64 _eulerNum = (deltaPress * pow_4(wheelDia)) / (_rhoAir * pow_2(_volFlow)); //[-]
    2215       48122 :         Real64 _normalizedEulerNum = std::log10(_eulerNum / eulerMaxEff);
    2216       48122 :         if (_normalizedEulerNum <= 0.0) {
    2217       14568 :             wheelEff = Curve::CurveValue(state, plTotalEffNormCurveNum, _normalizedEulerNum);
    2218             :         } else {
    2219       33554 :             wheelEff = Curve::CurveValue(state, plTotalEffStallCurveNum, _normalizedEulerNum);
    2220             :         }
    2221       48122 :         wheelEff *= maxEff;             // [-]
    2222       48122 :         wheelEff = max(wheelEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
    2223             : 
    2224             :         // Calculate fan shaft power using fan static air power and fan static efficiency
    2225       48122 :         shaftPower = airPower / wheelEff; //[W]
    2226             : 
    2227             :         // Calculate fan shaft speed, fan torque, and motor speed using Wray dimensionless fan airflow model
    2228       48122 :         if (_normalizedEulerNum <= 0.0) {
    2229       14568 :             _dimFlow = Curve::CurveValue(state, dimFlowNormCurveNum, _normalizedEulerNum); //[-]
    2230             :         } else {
    2231       33554 :             _dimFlow = Curve::CurveValue(state, dimFlowStallCurveNum, _normalizedEulerNum); //[-]
    2232             :         }
    2233       48122 :         Real64 _speedRadS = _volFlow / (_dimFlow * maxDimFlow * pow_3(wheelDia)); //[rad/s]
    2234       48122 :         fanTorque = shaftPower / _speedRadS;                                      //[N-m]
    2235       48122 :         fanSpeed = _speedRadS * 9.549296586;                                      //[rpm, conversion factor is 30/PI]
    2236       48122 :         Real64 _motorSpeed = fanSpeed * pulleyDiaRatio;                           //[rpm]
    2237             : 
    2238             :         // Calculate belt part-load drive efficiency using correlations and coefficients based on ACEEE data
    2239             :         // Direct-drive is represented using curve coefficients such that "belt" max eff and PL eff = 1.0
    2240       48122 :         Real64 _torqueRatio = fanTorque / beltMaxTorque; //[-]
    2241       48122 :         if ((_torqueRatio <= beltTorqueTrans) && (plBeltEffReg1CurveNum != 0)) {
    2242        3688 :             _beltPLEff = Curve::CurveValue(state, plBeltEffReg1CurveNum, _torqueRatio); //[-]
    2243       44434 :         } else if ((_torqueRatio > beltTorqueTrans) && (_torqueRatio <= 1.0) && (plBeltEffReg2CurveNum != 0)) {
    2244       44434 :             _beltPLEff = Curve::CurveValue(state, plBeltEffReg2CurveNum, _torqueRatio); //[-]
    2245           0 :         } else if ((_torqueRatio > 1.0) && (plBeltEffReg3CurveNum != 0)) {
    2246           0 :             _beltPLEff = Curve::CurveValue(state, plBeltEffReg3CurveNum, _torqueRatio); //[-]
    2247             :         } else {
    2248           0 :             _beltPLEff = 1.0; // Direct drive or no curve specified - use constant efficiency
    2249             :         }
    2250       48122 :         beltEff = beltMaxEff * _beltPLEff; //[-]
    2251       48122 :         beltEff = max(beltEff, 0.01);      // Minimum efficiency is 1% to avoid numerical errors
    2252             : 
    2253             :         // Calculate belt input power using fan shaft power and belt efficiency
    2254       48122 :         beltInputPower = shaftPower / beltEff; //[W]
    2255             : 
    2256             :         // Calculate motor part-load efficiency using correlations and coefficients based on MotorMaster+ data
    2257       48122 :         Real64 _motorOutPowerRatio = beltInputPower / motorMaxOutPower; //[-]
    2258       48122 :         if (plMotorEffCurveNum != 0) {
    2259       48122 :             _motorPLEff = Curve::CurveValue(state, plMotorEffCurveNum, _motorOutPowerRatio); //[-]
    2260             :         } else {
    2261           0 :             _motorPLEff = 1.0; // No curve specified - use constant efficiency
    2262             :         }
    2263       48122 :         motorEff = motorMaxEff * _motorPLEff; //[-]
    2264       48122 :         motorEff = max(motorEff, 0.01);       // Minimum efficiency is 1% to avoid numerical errors
    2265             : 
    2266             :         // Calculate motor input power using belt input power and motor efficiency
    2267       48122 :         motorInputPower = beltInputPower / motorEff; //[W]
    2268             : 
    2269             :         // Calculate VFD efficiency using correlations and coefficients based on VFD type
    2270       48122 :         if ((vfdEffType == VFDEffType::Speed) && (vfdEffCurveNum != 0)) {
    2271           0 :             _vfdSpeedRatio = _motorSpeed / motorMaxSpeed;                      //[-]
    2272           0 :             vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdSpeedRatio); //[-]
    2273       48122 :         } else if ((vfdEffType == VFDEffType::Power) && (vfdEffCurveNum != 0)) {
    2274       48122 :             _vfdOutPowerRatio = motorInputPower / vfdMaxOutPower;                 //[-]
    2275       48122 :             vfdEff = Curve::CurveValue(state, vfdEffCurveNum, _vfdOutPowerRatio); //[-]
    2276             :         } else {
    2277             :             // No curve specified - use constant efficiency
    2278           0 :             vfdMaxOutPower = 0.0;
    2279           0 :             vfdEff = 0.97;
    2280             :         }
    2281       48122 :         vfdEff = max(vfdEff, 0.01); // Minimum efficiency is 1% to avoid numerical errors
    2282             : 
    2283             :         // Calculate VFD input power using motor input power and VFD efficiency
    2284       48122 :         vfdInputPower = motorInputPower / vfdEff; //[W]
    2285       48122 :         totalPower = vfdInputPower;               //[W]
    2286             : 
    2287             :         // Calculate combined fan system efficiency: includes fan, belt, motor, and VFD
    2288             :         // Equivalent to fan%FanAirPower / fan%FanPower
    2289       48122 :         totalEff = wheelEff * beltEff * motorEff * vfdEff;
    2290             : 
    2291             :         //    IF (fan%EMSFanEffOverrideOn) FanEff = fan%EMSFanEffValue
    2292             : 
    2293             :         // Calculate air enthalpy and temperature rise from power entering air stream from fan wheel, belt, and motor
    2294             :         // Assumes MotInAirFrac applies to belt and motor but NOT to VFD
    2295       48122 :         powerLossToAir = shaftPower + (motorInputPower - shaftPower) * motorInAirFrac; //[W]
    2296       48122 :         outletAirEnthalpy = inletAirEnthalpy + (powerLossToAir / _massFlow);           //[kJ/kg]
    2297             : 
    2298             :         // This fan does not change the moisture or mass flow across the component
    2299       48122 :         outletAirHumRat = inletAirHumRat;  //[-]
    2300       48122 :         outletAirMassFlowRate = _massFlow; //[kg/s]
    2301       48122 :         outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    2302             :     } else {
    2303             :         // Fan is OFF and not operating -- no power consumed and zero mass flow rate
    2304       14546 :         totalPower = 0.0;
    2305       14546 :         shaftPower = 0.0;
    2306       14546 :         powerLossToAir = 0.0;
    2307       14546 :         outletAirMassFlowRate = 0.0;
    2308       14546 :         outletAirHumRat = inletAirHumRat;
    2309       14546 :         outletAirEnthalpy = inletAirEnthalpy;
    2310       14546 :         outletAirTemp = inletAirTemp;
    2311             :         // Set the Control Flow variables to 0.0 flow when OFF.
    2312       14546 :         massFlowRateMaxAvail = 0.0;
    2313       14546 :         massFlowRateMinAvail = 0.0;
    2314             : 
    2315       14546 :         deltaPress = 0.0;
    2316       14546 :         airPower = 0.0;
    2317       14546 :         wheelEff = 0.0;
    2318       14546 :         fanSpeed = 0.0;
    2319       14546 :         fanTorque = 0.0;
    2320       14546 :         beltEff = 0.0;
    2321       14546 :         beltInputPower = 0.0;
    2322       14546 :         motorEff = 0.0;
    2323       14546 :         motorInputPower = 0.0;
    2324       14546 :         vfdEff = 0.0;
    2325       14546 :         vfdInputPower = 0.0;
    2326       14546 :         totalEff = 0.0;
    2327             :     }
    2328       62668 : } // FanComponent::simulateComponentModel()
    2329             : 
    2330   145368856 : void FanComponent::update(EnergyPlusData &state)
    2331             : {
    2332             : 
    2333             :     // SUBROUTINE INFORMATION:
    2334             :     //       AUTHOR         Richard Liesen
    2335             :     //       DATE WRITTEN   April 1998
    2336             :     //       MODIFIED       L. Gu, Feb. 1, 2007, No unbalance airflow when Zone Exhaust Fans are used in the AirflowNetwork
    2337             : 
    2338             :     // PURPOSE OF THIS SUBROUTINE:
    2339             :     // This subroutine updates the fan outlet nodes.
    2340             : 
    2341             :     // METHODOLOGY EMPLOYED:
    2342             :     // Data is moved from the fan data structure to the fan outlet nodes.
    2343             : 
    2344   145368856 :     auto &inletNode = state.dataLoopNodes->Node(inletNodeNum);
    2345   145368856 :     auto &outletNode = state.dataLoopNodes->Node(outletNodeNum);
    2346             : 
    2347             :     // Set the outlet air nodes of the fan
    2348   145368856 :     outletNode.MassFlowRate = outletAirMassFlowRate;
    2349   145368856 :     outletNode.Temp = outletAirTemp;
    2350   145368856 :     outletNode.HumRat = outletAirHumRat;
    2351   145368856 :     outletNode.Enthalpy = outletAirEnthalpy;
    2352             :     // Set the outlet nodes for properties that just pass through & not used
    2353   145368856 :     outletNode.Quality = inletNode.Quality;
    2354   145368856 :     outletNode.Press = inletNode.Press;
    2355             : 
    2356             :     // Set the Node Flow Control Variables from the Fan Control Variables
    2357   145368856 :     outletNode.MassFlowRateMaxAvail = massFlowRateMaxAvail;
    2358   145368856 :     outletNode.MassFlowRateMinAvail = massFlowRateMinAvail;
    2359             : 
    2360   145368856 :     if (type == HVAC::FanType::Exhaust) {
    2361     1543429 :         inletNode.MassFlowRate = inletAirMassFlowRate;
    2362     1543429 :         if (state.afn->AirflowNetworkNumOfExhFan == 0) {
    2363     1348314 :             state.dataHVACGlobal->UnbalExhMassFlow = inletAirMassFlowRate;
    2364     1348314 :             if (balancedFractSchedNum > 0) {
    2365      669548 :                 state.dataHVACGlobal->BalancedExhMassFlow =
    2366      334774 :                     state.dataHVACGlobal->UnbalExhMassFlow * ScheduleManager::GetCurrentScheduleValue(state, balancedFractSchedNum);
    2367      334774 :                 state.dataHVACGlobal->UnbalExhMassFlow = state.dataHVACGlobal->UnbalExhMassFlow - state.dataHVACGlobal->BalancedExhMassFlow;
    2368             :             } else {
    2369     1013540 :                 state.dataHVACGlobal->BalancedExhMassFlow = 0.0;
    2370             :             }
    2371             :         } else {
    2372      195115 :             state.dataHVACGlobal->UnbalExhMassFlow = 0.0;
    2373      195115 :             state.dataHVACGlobal->BalancedExhMassFlow = 0.0;
    2374             :         }
    2375     1543429 :         unbalancedOutletMassFlowRate = state.dataHVACGlobal->UnbalExhMassFlow;
    2376     1543429 :         balancedOutletMassFlowRate = state.dataHVACGlobal->BalancedExhMassFlow;
    2377             :     }
    2378             : 
    2379   145368856 :     if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    2380      909320 :         outletNode.CO2 = inletNode.CO2;
    2381             :     }
    2382             : 
    2383   145368856 :     if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    2384      237340 :         outletNode.GenContam = inletNode.GenContam;
    2385             :     }
    2386   145368856 : }
    2387             : 
    2388   145368856 : void FanComponent::report(EnergyPlusData &state)
    2389             : {
    2390             : 
    2391             :     // SUBROUTINE INFORMATION:
    2392             :     //       AUTHOR         Richard Liesen
    2393             :     //       DATE WRITTEN   April 1998
    2394             : 
    2395             :     // PURPOSE OF THIS SUBROUTINE:
    2396             :     // This subroutine updates the report variables for the fans.
    2397             : 
    2398   145368856 :     totalEnergy = totalPower * state.dataHVACGlobal->TimeStepSysSec;
    2399   145368856 :     deltaTemp = outletAirTemp - inletAirTemp;
    2400             : 
    2401   145368856 :     if (isAFNFan && (airLoopNum > 0)) {
    2402     2344597 :         if (type == HVAC::FanType::OnOff) {
    2403     2052652 :             state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = runtimeFrac;
    2404             :         }
    2405             :     }
    2406   145368856 : } // FanComponent::report()
    2407             : 
    2408        5466 : int GetFanIndex(EnergyPlusData &state, std::string const &FanName)
    2409             : {
    2410             : 
    2411             :     // SUBROUTINE INFORMATION:
    2412             :     //       AUTHOR         Linda Lawrie
    2413             :     //       DATE WRITTEN   June 2004
    2414             : 
    2415             :     // PURPOSE OF THIS SUBROUTINE:
    2416             :     // This subroutine sets an index for a given fan -- issues error message if that fan
    2417             :     // is not legal fan.
    2418             : 
    2419        5466 :     if (state.dataFans->GetFanInputFlag) { // First time subroutine has been entered
    2420         596 :         GetFanInput(state);
    2421         596 :         state.dataFans->GetFanInputFlag = false;
    2422             :     }
    2423             : 
    2424        5466 :     auto found = state.dataFans->fanMap.find(FanName);
    2425       10932 :     return (found == state.dataFans->fanMap.end()) ? 0 : found->second;
    2426        5466 : } // GetFanIndex()
    2427             : 
    2428        2568 : Real64 CalFaultyFanAirFlowReduction(EnergyPlusData &state,
    2429             :                                     std::string const &FanName,          // name of the fan
    2430             :                                     Real64 const FanDesignAirFlowRate,   // Fan Design Volume Flow Rate [m3/sec]
    2431             :                                     Real64 const FanDesignDeltaPress,    // Fan Design Delta Pressure [Pa]
    2432             :                                     Real64 const FanFaultyDeltaPressInc, // Increase of Fan Delta Pressure in the Faulty Case [Pa]
    2433             :                                     int const FanCurvePtr                // Fan Curve Index
    2434             : )
    2435             : {
    2436             : 
    2437             :     // SUBROUTINE INFORMATION:
    2438             :     //       AUTHOR         Rongpeng Zhang
    2439             :     //       DATE WRITTEN   Apr. 2015
    2440             : 
    2441             :     // PURPOSE OF THIS SUBROUTINE:
    2442             :     // Calculate the decrease of the fan air flow rate, given the fan curve
    2443             :     // and the increase of fan pressure rise due to fouling air filters
    2444             : 
    2445             :     // Check whether the fan curve covers the design operational point of the fan
    2446        2568 :     Real64 FanCalDeltaPress = Curve::CurveValue(state, FanCurvePtr, FanDesignAirFlowRate); // [Pa]
    2447        2568 :     if ((FanCalDeltaPress < 0.9 * FanDesignDeltaPress) || (FanCalDeltaPress > 1.1 * FanDesignDeltaPress)) {
    2448           0 :         ShowWarningError(state, format("The design operational point of the fan {} does not fall ", FanName));
    2449           0 :         ShowContinueError(state, "on the fan curve provided in the FaultModel:Fouling:AirFilter object. ");
    2450           0 :         return 0.0;
    2451             :     }
    2452             : 
    2453             :     // Calculate the Fan Volume Flow Rate in the Faulty Case
    2454        2568 :     Real64 FanFaultyAirFlowRate = FanDesignAirFlowRate;                                        // Fan Volume Flow Rate in the Faulty Case [m3/sec]
    2455        2568 :     Real64 FanCalDeltaPresstemp = Curve::CurveValue(state, FanCurvePtr, FanFaultyAirFlowRate); // Calculated Fan Delta Pressure for temp use [Pa]
    2456        2568 :     FanCalDeltaPress = FanCalDeltaPresstemp;
    2457             : 
    2458      816624 :     while (FanCalDeltaPress < (FanDesignDeltaPress + FanFaultyDeltaPressInc)) {
    2459      814056 :         FanFaultyAirFlowRate = FanFaultyAirFlowRate - 0.005;
    2460      814056 :         FanCalDeltaPresstemp = Curve::CurveValue(state, FanCurvePtr, FanFaultyAirFlowRate);
    2461             : 
    2462     1628112 :         if ((FanCalDeltaPresstemp <= FanCalDeltaPress) ||
    2463      814056 :             (FanFaultyAirFlowRate <= state.dataCurveManager->PerfCurve(FanCurvePtr)->inputLimits[0].min)) {
    2464             :             // The new operational point of the fan go beyond the fan selection range
    2465           0 :             ShowWarningError(state, format("The operational point of the fan {} may go beyond the fan selection ", FanName));
    2466           0 :             ShowContinueError(state, "range in the faulty fouling air filter cases");
    2467           0 :             break;
    2468             :         }
    2469             : 
    2470      814056 :         FanCalDeltaPress = FanCalDeltaPresstemp;
    2471             :     }
    2472             : 
    2473        2568 :     return FanDesignAirFlowRate - FanFaultyAirFlowRate;
    2474             : }
    2475             : 
    2476         374 : Real64 FanComponent::getDesignHeatGain(EnergyPlusData &state,
    2477             :                                        Real64 const _volFlow // fan volumetric flow rate [m3/s]
    2478             : )
    2479             : {
    2480             :     // FUNCTION INFORMATION:
    2481             :     //       AUTHOR         Fred Buhl
    2482             :     //       DATE WRITTEN   August 2014
    2483             : 
    2484             :     // PURPOSE OF THIS FUNCTION:
    2485             :     // This function calculates and returns the design fan heat gain from the fan input data
    2486             : 
    2487             :     // METHODOLOGY EMPLOYED:
    2488             :     // Simple fan:  Qdot,tot = (Vdot*deltaP)/Eff,tot
    2489             :     //              Qdot,air = Eff,mot*Qdot,tot + (Qdot,tot - Eff,mot*Qdot,tot)*Frac,mot-in-airstream
    2490             : 
    2491         374 :     if (type != HVAC::FanType::ComponentModel) {
    2492         374 :         Real64 _deltaP = deltaPress; // fan design pressure rise [N/m2]
    2493         374 :         Real64 _totalEff = totalEff; // fan design total efficiency
    2494         374 :         Real64 _motorEff = motorEff; // fan design motor efficiency
    2495         374 :         Real64 _motorInAirFrac = motorInAirFrac;
    2496         374 :         Real64 _powerTot = (_volFlow * _deltaP) / _totalEff;
    2497         374 :         return _motorEff * _powerTot + (_powerTot - _motorEff * _powerTot) * _motorInAirFrac;
    2498             :     } else {
    2499           0 :         if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
    2500           0 :             set_size(state);
    2501           0 :             sizingFlag = false;
    2502             :         }
    2503           0 :         return shaftPower + (motorInputPower - shaftPower) * motorInAirFrac;
    2504             :     }
    2505             : } // FanComponent::getDesignHeatGain()
    2506             : 
    2507       12122 : void FanComponent::getInputsForDesignHeatGain(EnergyPlusData &state,
    2508             :                                               Real64 &_deltaP,
    2509             :                                               Real64 &_motEff,
    2510             :                                               Real64 &_totEff,
    2511             :                                               Real64 &_motInAirFrac,
    2512             :                                               Real64 &_fanShaftPow,
    2513             :                                               Real64 &_motInPower,
    2514             :                                               bool &_fanCompModel)
    2515             : {
    2516       12122 :     if (type != HVAC::FanType::ComponentModel) {
    2517       12111 :         _deltaP = deltaPress;
    2518       12111 :         _motEff = motorEff;
    2519       12111 :         _totEff = totalEff;
    2520       12111 :         _motInAirFrac = motorInAirFrac;
    2521       12111 :         _fanShaftPow = 0.0;
    2522       12111 :         _motInPower = 0.0;
    2523       12111 :         _fanCompModel = false;
    2524             :     } else {
    2525          11 :         _deltaP = 0.0;
    2526          11 :         _motEff = 0.0;
    2527          11 :         _totEff = 0.0;
    2528          11 :         if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
    2529           1 :             set_size(state);
    2530           1 :             sizingFlag = false;
    2531             :         }
    2532          11 :         _fanCompModel = true;
    2533          11 :         _fanShaftPow = shaftPower;
    2534          11 :         _motInPower = motorInputPower;
    2535          11 :         _motInAirFrac = motorInAirFrac;
    2536             :     }
    2537       12122 : } // FanComponent::getInputsForDesHeatGain()
    2538             : 
    2539    10763058 : void FanSystem::init(EnergyPlusData &state)
    2540             : {
    2541    10763058 :     if (!state.dataGlobal->SysSizingCalc && sizingFlag) {
    2542          73 :         set_size(state);
    2543          73 :         sizingFlag = false;
    2544             :     }
    2545             : 
    2546    10763058 :     auto &outletNode = state.dataLoopNodes->Node(outletNodeNum);
    2547    10763058 :     auto &inletNode = state.dataLoopNodes->Node(inletNodeNum);
    2548             : 
    2549    10763058 :     if (state.dataGlobal->BeginEnvrnFlag && envrnFlag) {
    2550             : 
    2551             :         // Currently, fan does not force minimum mass flow, only used for power calculation
    2552             :         // m_minAirFlowRate = designAirVolFlowRate * m_minPowerFlowFrac;
    2553             :         // m_minAirMassFlowRate = m_minAirFlowRate * m_rhoAirStdInit;
    2554             : 
    2555             :         // Init the Node Control variables
    2556        1340 :         outletNode.MassFlowRateMax = maxAirMassFlowRate;
    2557             :         // Currently, fan does not force minimum mass flow, only used for power calculation
    2558             :         // DataLoopNode::Node( outletNodeNum ).MassFlowRateMin = m_minAirMassFlowRate;
    2559             : 
    2560             :         // Initialize all report variables to a known state at beginning of simulation
    2561        1340 :         totalPower = 0.0;
    2562        1340 :         deltaTemp = 0.0;
    2563        1340 :         powerLossToAir = 0.0;
    2564        1340 :         totalEnergy = 0.0;
    2565        2847 :         for (int iSpeed = 0; iSpeed < numSpeeds; ++iSpeed) {
    2566        1507 :             runtimeFracAtSpeed[iSpeed] = 0.0;
    2567             :         }
    2568        1340 :         envrnFlag = false;
    2569             :     }
    2570             : 
    2571    10763058 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    2572    10719597 :         envrnFlag = true;
    2573             :     }
    2574             : 
    2575    10763058 :     massFlowRateMaxAvail = min(outletNode.MassFlowRateMax, inletNode.MassFlowRateMaxAvail);
    2576    10763058 :     massFlowRateMinAvail = min(max(outletNode.MassFlowRateMin, inletNode.MassFlowRateMinAvail), inletNode.MassFlowRateMaxAvail);
    2577             : 
    2578             :     // Load the node data in this section for the component simulation
    2579             :     // First need to make sure that the MassFlowRate is between the max and min avail.
    2580    10763058 :     inletAirMassFlowRate = min(inletNode.MassFlowRate, massFlowRateMaxAvail);
    2581    10763058 :     inletAirMassFlowRate = max(inletAirMassFlowRate, massFlowRateMinAvail);
    2582             : 
    2583             :     // Then set the other conditions
    2584    10763058 :     inletAirTemp = inletNode.Temp;
    2585    10763058 :     inletAirHumRat = inletNode.HumRat;
    2586    10763058 :     inletAirEnthalpy = inletNode.Enthalpy;
    2587    10763058 : } // FanSystem::init()
    2588             : 
    2589         229 : void FanSystem::set_size(EnergyPlusData &state)
    2590             : {
    2591             :     static constexpr std::string_view routineName = "FanSystem::set_size";
    2592             : 
    2593         229 :     Real64 _tempFlow = maxAirFlowRate;
    2594         229 :     bool _bPRINT = true;
    2595         229 :     state.dataSize->DataAutosizable = true;
    2596         229 :     state.dataSize->DataEMSOverrideON = EMSMaxAirFlowRateOverrideOn;
    2597         229 :     state.dataSize->DataEMSOverride = EMSMaxAirFlowRateValue;
    2598         229 :     airLoopNum = state.dataSize->CurSysNum;
    2599             : 
    2600         229 :     bool ErrorsFound = false;
    2601         229 :     SystemAirFlowSizer sizerSystemAirFlow;
    2602         229 :     sizerSystemAirFlow.initializeWithinEP(state, HVAC::fanTypeNames[(int)type], Name, _bPRINT, routineName);
    2603         229 :     maxAirFlowRate = sizerSystemAirFlow.size(state, _tempFlow, ErrorsFound);
    2604             : 
    2605         229 :     state.dataSize->DataAutosizable = true; // should be false?
    2606         229 :     state.dataSize->DataEMSOverrideON = false;
    2607         229 :     state.dataSize->DataEMSOverride = 0.0;
    2608             : 
    2609         229 :     if (designElecPowerWasAutosized) {
    2610             : 
    2611         228 :         switch (powerSizingMethod) {
    2612           0 :         case PowerSizing::PerFlow: {
    2613           0 :             designElecPower = maxAirFlowRate * elecPowerPerFlowRate;
    2614           0 :         } break;
    2615          21 :         case PowerSizing::PerFlowPerPressure: {
    2616          21 :             designElecPower = maxAirFlowRate * deltaPress * elecPowerPerFlowRatePerPressure;
    2617          21 :         } break;
    2618         207 :         case PowerSizing::TotalEfficiencyAndPressure: {
    2619         207 :             designElecPower = maxAirFlowRate * deltaPress / totalEff;
    2620         207 :         } break;
    2621           0 :         case PowerSizing::Invalid: {
    2622             :             // do nothing (no assert?)
    2623           0 :             break;
    2624             :         }
    2625           0 :         default:
    2626           0 :             assert(false);
    2627             : 
    2628             :         } // end switch
    2629             : 
    2630             :         // report design power
    2631         228 :         BaseSizer::reportSizerOutput(state, HVAC::fanTypeNames[(int)type], Name, "Design Electric Power Consumption [W]", designElecPower);
    2632             : 
    2633             :     } // end if power was autosized
    2634             : 
    2635         229 :     rhoAirStdInit = state.dataEnvrn->StdRhoAir;
    2636         229 :     maxAirMassFlowRate = maxAirFlowRate * rhoAirStdInit;
    2637             : 
    2638             :     // calculate total fan system efficiency at design, else set to 1 to avoid div by zero
    2639         229 :     totalEff = (designElecPower > 0.0) ? maxAirFlowRate * deltaPress / designElecPower : 1.0;
    2640             : 
    2641         229 :     if (speedControl == SpeedControl::Discrete && numSpeeds > 1) { // set up values at speeds
    2642          22 :         massFlowAtSpeed.resize(numSpeeds, 0.0);
    2643          22 :         totalEffAtSpeed.resize(numSpeeds, 0.0);
    2644          69 :         for (int loop = 0; loop < numSpeeds; ++loop) {
    2645          47 :             massFlowAtSpeed[loop] = maxAirMassFlowRate * flowFracAtSpeed[loop];
    2646          47 :             if (powerFracInputAtSpeed[loop]) { // use speed power fraction
    2647          47 :                 if (designElecPower > 0.0) {
    2648          47 :                     totalEffAtSpeed[loop] = flowFracAtSpeed[loop] * maxAirFlowRate * deltaPress / (designElecPower * powerFracAtSpeed[loop]);
    2649             :                 } else {
    2650           0 :                     totalEffAtSpeed[loop] = 1.0;
    2651             :                 }
    2652             :             } else { // use power curve
    2653           0 :                 totalEffAtSpeed[loop] = flowFracAtSpeed[loop] * maxAirFlowRate * deltaPress /
    2654           0 :                                         (designElecPower * Curve::CurveValue(state, powerModFuncFlowFracCurveNum, flowFracAtSpeed[loop]));
    2655           0 :                 powerFracAtSpeed[loop] = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, flowFracAtSpeed[loop]);
    2656             :             }
    2657             :         }
    2658             :     }
    2659         229 :     designPointFEI = report_fei(state, maxAirFlowRate, designElecPower, deltaPress);
    2660             : 
    2661         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, Name, HVAC::fanTypeNames[(int)type]);
    2662         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, Name, totalEff);
    2663         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, Name, deltaPress);
    2664         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, Name, maxAirFlowRate);
    2665             : 
    2666         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, Name, designElecPower);
    2667         229 :     if (maxAirFlowRate != 0.0) {
    2668         229 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwrPerFlow, Name, designElecPower / maxAirFlowRate);
    2669             :     }
    2670         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, Name, motorInAirFrac);
    2671         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, Name, designPointFEI);
    2672             : 
    2673         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, Name, endUseSubcategoryName);
    2674             : 
    2675         229 :     sizingFlag = false;
    2676             : 
    2677             :     // Std 229 Fans (HVACFan.cc)
    2678         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPurpose, Name, "N/A"); // m_fanType); // purpose? not the same
    2679         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAutosized, Name, maxAirFlowRateIsAutosized ? "Yes" : "No");
    2680         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorEff, Name, motorEff);
    2681         229 :     OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorHeatToZoneFrac, Name, 1 - motorInAirFrac);
    2682         458 :     OutputReportPredefined::PreDefTableEntry(state,
    2683         229 :                                              state.dataOutRptPredefined->pdchFanMotorHeatZone,
    2684             :                                              Name,
    2685         458 :                                              heatLossDest == HeatLossDest::Zone ? state.dataHeatBal->Zone(zoneNum).Name : "N/A");
    2686         229 :     if (airLoopNum == 0) {
    2687         170 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, "N/A");
    2688          59 :     } else if (airLoopNum <= state.dataHVACGlobal->NumPrimaryAirSys) {
    2689         116 :         OutputReportPredefined::PreDefTableEntry(
    2690         116 :             state, state.dataOutRptPredefined->pdchFanAirLoopName, Name, state.dataAirSystemsData->PrimaryAirSystems(airLoopNum).Name);
    2691             :     } else {
    2692           2 :         OutputReportPredefined::PreDefTableEntry(
    2693             :             state,
    2694           1 :             state.dataOutRptPredefined->pdchFanAirLoopName,
    2695             :             Name,
    2696           1 :             state.dataAirLoopHVACDOAS->airloopDOAS[airLoopNum - state.dataHVACGlobal->NumPrimaryAirSys - 1].Name);
    2697             :     }
    2698         229 : }
    2699             : 
    2700        2207 : Real64 FanSystem::report_fei(EnergyPlusData &state, Real64 const _designFlowRate, Real64 const _designElecPower, Real64 const _designDeltaPress)
    2701             : {
    2702             :     // PURPOSE OF THIS SUBROUTINE:
    2703             :     // Calculate the Fan Energy Index
    2704             : 
    2705             :     // REFERENCES:
    2706             :     // ANSI/AMCA Standard 207-17: Fan System Efficiency and Fan System Input Power Calculation, 2017.
    2707             :     // AANSI / AMCA Standard 208 - 18: Calculation of the Fan Energy Index, 2018.
    2708             : 
    2709        2207 :     Real64 constexpr rhoAirStd = 1.2;   // Value from the above referenced standard
    2710        2207 :     Real64 constexpr tempAirFan = 21.0; // Standard fan inlet temperature in Celsius
    2711        2207 :     Real64 constexpr hrAirFan = 0.5;    // Standard fan inlet humidity ratio (50%)
    2712        2207 :     Real64 _wAirFan = Psychrometrics::PsyWFnTdbRhPb(state, tempAirFan, hrAirFan, state.dataEnvrn->StdBaroPress);
    2713        2207 :     Real64 _rhoAirFan = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, tempAirFan, _wAirFan);
    2714             : 
    2715             :     // Calculate reference fan shaft power
    2716        2207 :     Real64 _refFanShaftPower = (_designFlowRate + 0.118) * (_designDeltaPress + 100 * _rhoAirFan / rhoAirStd) / (1000 * 0.66);
    2717             : 
    2718             :     // Calculate reference reference fan transmission efficiency
    2719        2207 :     Real64 _refFanTransEff = 0.96 * pow((_refFanShaftPower / (_refFanShaftPower + 1.64)), 0.05);
    2720             : 
    2721             :     // Calculate reference reference fan motor efficiency
    2722        2207 :     Real64 _refFanMotorOutput = _refFanShaftPower / _refFanTransEff;
    2723             : 
    2724             :     Real64 _refFanMotorEff;
    2725        2207 :     if (_refFanMotorOutput < 185.0) {
    2726        2176 :         _refFanMotorEff = -0.003812 * pow(std::log10(_refFanMotorOutput), 4) + 0.025834 * pow(std::log10(_refFanMotorOutput), 3) -
    2727        2176 :                           0.072577 * pow(std::log10(_refFanMotorOutput), 2) + 0.125559 * std::log10(_refFanMotorOutput) + 0.850274;
    2728             :     } else {
    2729          31 :         _refFanMotorEff = 0.962;
    2730             :     }
    2731             : 
    2732             :     // Calculate reference reference fan motor controller  efficiency
    2733        2207 :     Real64 _refFanMotorCtrlEff = 1;
    2734             : 
    2735        2207 :     Real64 _refFanElecPower = _refFanShaftPower / (_refFanTransEff * _refFanMotorEff * _refFanMotorCtrlEff);
    2736             : 
    2737        2207 :     return (_designElecPower > 0.0) ? (_refFanElecPower * 1000 / _designElecPower) : 0.0;
    2738             : } // FanSystem::report_fei()
    2739             : 
    2740    10762717 : void FanSystem::calcSimpleSystemFan(
    2741             :     EnergyPlusData &state,
    2742             :     ObjexxFCL::Optional<Real64 const> _flowFraction, // Flow fraction for entire timestep (not used if flow ratios are present)
    2743             :     ObjexxFCL::Optional<Real64 const> _pressureRise, // Pressure difference to use for DeltaPress
    2744             :     ObjexxFCL::Optional<Real64 const> _flowRatio1,   // Flow ratio in operating mode 1
    2745             :     ObjexxFCL::Optional<Real64 const> _runTimeFrac1, // Run time fraction in operating mode 1
    2746             :     ObjexxFCL::Optional<Real64 const> _flowRatio2,   // Flow ratio in operating mode 2
    2747             :     ObjexxFCL::Optional<Real64 const> _runTimeFrac2, // Run time fraction in operating mode 2
    2748             :     ObjexxFCL::Optional<Real64 const> _pressureRise2 // Pressure difference to use for operating mode 2
    2749             : )
    2750             : {
    2751             :     Real64 _localFlowFrac;
    2752             :     Real64 _localTotalEff;
    2753    10762717 :     std::array<Real64, 2> _localPressureRise = {0.0, 0.0}; // [0] is operating mode 1, [1] is operating mode 2
    2754    10762717 :     std::array<Real64, 2> _localAirMassFlow = {0.0, 0.0};
    2755    10762717 :     std::array<Real64, 2> _localFlowRatio = {0.0, 0.0};
    2756    10762717 :     std::array<Real64, 2> _localRuntimeFrac = {1.0, 1.0};
    2757    10762717 :     bool _useFlowRatiosAndRunTimeFracs = false;
    2758             : 
    2759             :     // Number of operating modes, 1 or 2 ( e.g. heating, ventilating, cooling)
    2760    10762717 :     int _numModes = (present(_flowRatio2) && present(_runTimeFrac2)) ? 2 : 1;
    2761             : 
    2762    10762717 :     if (state.dataHVACGlobal->NightVentOn) {
    2763             :         // assume if non-zero inputs for night data then this fan is to be used with that data
    2764           0 :         if (nightVentPressureDelta > 0.0) {
    2765           0 :             _localPressureRise[0] = nightVentPressureDelta;
    2766           0 :             _localPressureRise[1] = nightVentPressureDelta;
    2767             :         }
    2768             : 
    2769           0 :         _localFlowFrac = (maxAirMassFlowRate > 0.0) ? inletAirMassFlowRate / maxAirMassFlowRate : 1.0;
    2770             : 
    2771           0 :         _localAirMassFlow[0] = inletAirMassFlowRate;
    2772             : 
    2773             :     } else { // not in night mode
    2774    10762717 :         _localPressureRise[0] = present(_pressureRise) ? _pressureRise() : deltaPress;
    2775             : 
    2776    10762717 :         _localPressureRise[1] = present(_pressureRise2) ? _pressureRise2() : deltaPress;
    2777             : 
    2778    10762717 :         if (present(_flowFraction)) {
    2779     1321323 :             _localFlowFrac = _flowFraction;
    2780     1321323 :             _localAirMassFlow[0] = _localFlowFrac * maxAirMassFlowRate;
    2781             :         } else {
    2782     9441394 :             if (maxAirMassFlowRate > 0.0) { // protect div by 0
    2783     9441394 :                 _localFlowFrac = inletAirMassFlowRate / maxAirMassFlowRate;
    2784             :             } else {
    2785           0 :                 _localFlowFrac = 1.0;
    2786             :             }
    2787     9441394 :             _localAirMassFlow[0] = inletAirMassFlowRate;
    2788             :         }
    2789    10762717 :         if (present(_flowRatio1) && present(_flowRatio2) && present(_runTimeFrac1) && present(_runTimeFrac2)) {
    2790     5909487 :             _useFlowRatiosAndRunTimeFracs = true;
    2791     5909487 :             _localRuntimeFrac[0] = _runTimeFrac1;
    2792     5909487 :             _localRuntimeFrac[1] = _runTimeFrac2;
    2793     5909487 :             _localFlowRatio[0] = _flowRatio1;
    2794     5909487 :             _localAirMassFlow[0] = _localFlowRatio[0] * maxAirMassFlowRate * _localRuntimeFrac[0];
    2795     5909487 :             _localFlowRatio[1] = _flowRatio2;
    2796     5909487 :             _localAirMassFlow[1] = _localFlowRatio[1] * maxAirMassFlowRate * _localRuntimeFrac[1];
    2797             :         } else {
    2798     4853230 :             _localRuntimeFrac[0] = 1.0; // if runTimeFracs are not present, assume single-mode operation
    2799     4853230 :             _localRuntimeFrac[1] = 0.0; // if runTimeFracs are not present, assume single-mode operation
    2800             :         }
    2801             :     }
    2802             : 
    2803    10762717 :     Real64 _localFaultMaxAirMassFlow = 0.0;
    2804    10762717 :     bool _faultActive = false;
    2805    10762717 :     Real64 _localFaultPressureRise = 0.0;
    2806           0 :     if (faultyFilterFlag && (state.dataFaultsMgr->NumFaultyAirFilter > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
    2807    10762717 :         state.dataGlobal->DoWeathSim && (!EMSMaxMassFlowOverrideOn) && (!EMSPressureOverrideOn)) {
    2808           0 :         auto &fault = state.dataFaultsMgr->FaultsFouledAirFilters(faultyFilterIndex);
    2809           0 :         if (ScheduleManager::GetCurrentScheduleValue(state, fault.availSchedNum) > 0) {
    2810           0 :             _faultActive = true;
    2811           0 :             Real64 _pressFrac = ScheduleManager::GetCurrentScheduleValue(state, fault.pressFracSchedNum);
    2812             :             Real64 _designFlowRateDec = // Decrease of the Fan Design Volume Flow Rate [m3/sec]
    2813           0 :                 Fans::CalFaultyFanAirFlowReduction(state, Name, maxAirFlowRate, deltaPress, (_pressFrac - 1) * deltaPress, fault.fanCurveNum);
    2814             : 
    2815           0 :             _localFaultMaxAirMassFlow = maxAirMassFlowRate - _designFlowRateDec * rhoAirStdInit;
    2816           0 :             _localFaultPressureRise = _pressFrac * deltaPress;
    2817             :         }
    2818             :     }
    2819             : 
    2820    27434921 :     for (int mode = 0; mode < _numModes; ++mode) {
    2821             :         // EMS override MassFlow, DeltaPress, and FanEff
    2822    16672204 :         if (EMSPressureOverrideOn) _localPressureRise[mode] = EMSPressureValue;
    2823    16672204 :         if (EMSTotalEffOverrideOn) _localTotalEff = EMSTotalEffValue;
    2824    16672204 :         if (EMSMaxMassFlowOverrideOn) _localAirMassFlow[mode] = EMSAirMassFlowValue;
    2825             : 
    2826    16672204 :         _localAirMassFlow[mode] = min(_localAirMassFlow[mode], maxAirMassFlowRate);
    2827    16672204 :         if (_faultActive) {
    2828           0 :             _localAirMassFlow[mode] = min(_localAirMassFlow[mode], _localFaultMaxAirMassFlow);
    2829           0 :             _localPressureRise[mode] = _localFaultPressureRise;
    2830             :         }
    2831    16672204 :         _localFlowFrac = _localAirMassFlow[0] / maxAirMassFlowRate;
    2832    16672204 :         _localFlowFrac = min(1.0, _localFlowFrac);
    2833             : 
    2834    16672204 :         if (_localRuntimeFrac[mode] > 0.0) {
    2835    10681011 :             _localFlowRatio[mode] = _localAirMassFlow[mode] / (maxAirMassFlowRate * _localRuntimeFrac[mode]);
    2836             :         }
    2837    16672204 :         _localFlowRatio[mode] = min(1.0, _localFlowRatio[mode]);
    2838             :     }
    2839             : 
    2840             :     // zero these now, because the may accumulate across multiple operating modes
    2841    10762717 :     powerLossToAir = 0.0;
    2842    10762717 :     totalPower = 0.0;
    2843    10762717 :     outletAirMassFlowRate = 0.0;
    2844    10762717 :     if (speedControl == SpeedControl::Discrete) {
    2845    20049413 :         for (int loop = 0; loop < numSpeeds; ++loop) {
    2846    10936361 :             runtimeFracAtSpeed[loop] = 0.0;
    2847             :         }
    2848             :     }
    2849             : 
    2850    11799541 :     if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedNum) > 0.0 || state.dataHVACGlobal->TurnFansOn) &&
    2851    11799541 :         !state.dataHVACGlobal->TurnFansOff && ((_localAirMassFlow[0] + _localAirMassFlow[1]) > 0.0)) {
    2852             :         // fan is running
    2853             : 
    2854    22391363 :         for (int mode = 0; mode < _numModes; ++mode) {
    2855             : 
    2856             :             // if no flow for this mode then continue to the next mode
    2857    13546528 :             if (_localAirMassFlow[mode] == 0.0) continue;
    2858             : 
    2859     9807434 :             switch (speedControl) {
    2860             : 
    2861     8351743 :             case SpeedControl::Discrete: {
    2862             :                 //
    2863     8351743 :                 if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
    2864           0 :                     state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0;
    2865             :                 }
    2866     8351743 :                 if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
    2867           0 :                     state.dataHVACGlobal->OnOffFanPartLoadFraction = 0.7; // a warning message is already issued from the DX coils or gas heating coil
    2868             :                 }
    2869     8351743 :                 if (_useFlowRatiosAndRunTimeFracs) {
    2870             :                     // Use flow ratios and runtimefractions pass from parent (allows fan to cycle at a specified speed)
    2871     4832347 :                     Real64 _locRuntimeFrac = (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0)
    2872     4832347 :                                                  ? _localRuntimeFrac[mode]
    2873      486967 :                                                  : max(0.0, min(1.0, _localRuntimeFrac[mode] / state.dataHVACGlobal->OnOffFanPartLoadFraction));
    2874     4832347 :                     Real64 _locFlowRatio = _localFlowRatio[mode]; // Current mode flow rate / max flow rate
    2875     4832347 :                     Real64 _locLowSpeedRuntimeFrac = 0.0;
    2876     4832347 :                     Real64 _locHiSpeedRuntimeFrac = 0.0;
    2877     4832347 :                     if (numSpeeds == 1) { // CV or OnOff
    2878     3198271 :                         _localTotalEff = totalEff;
    2879     3198271 :                         _locHiSpeedRuntimeFrac = _locRuntimeFrac * _locFlowRatio;
    2880     3198271 :                         runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
    2881     3198271 :                         totalPower +=
    2882     3198271 :                             max(0.0, _locHiSpeedRuntimeFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
    2883     1634076 :                     } else if (numSpeeds > 1) { // multi speed
    2884             : 
    2885             :                         // find which two speed levels bracket flow ratios and calculate runtimefraction at each speed
    2886             :                         // ideally the flow ratios passed in will match one of the fan m_flowFractionAtSpeed but it is not required
    2887     1634076 :                         int _lowSideSpeed = -1;
    2888     1634076 :                         int _hiSideSpeed = -1;
    2889             : 
    2890     1634076 :                         if (_locFlowRatio <= flowFracAtSpeed[0]) { // on/off at lowest speed
    2891      813042 :                             _hiSideSpeed = 0;
    2892      813042 :                             _locHiSpeedRuntimeFrac = _locFlowRatio * _locRuntimeFrac / flowFracAtSpeed[0];
    2893      813042 :                             runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
    2894             :                         } else {
    2895      821034 :                             _lowSideSpeed = 0; // hush up cppcheck
    2896      821034 :                             _hiSideSpeed = 0;  // hush up cppcheck
    2897      821034 :                             for (int loop = 0; loop < numSpeeds - 1; ++loop) {
    2898      821034 :                                 if ((flowFracAtSpeed[loop] <= _locFlowRatio) && (_locFlowRatio <= flowFracAtSpeed[loop + 1])) {
    2899      821034 :                                     _lowSideSpeed = loop;
    2900      821034 :                                     _hiSideSpeed = loop + 1;
    2901      821034 :                                     break;
    2902             :                                 }
    2903             :                             }
    2904             :                             Real64 _locLowSpeedTimeFrac =
    2905      821034 :                                 (flowFracAtSpeed[_hiSideSpeed] - _locFlowRatio) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
    2906      821034 :                             _locLowSpeedRuntimeFrac = _locLowSpeedTimeFrac * _localRuntimeFrac[mode];
    2907      821034 :                             _locHiSpeedRuntimeFrac = (1 - _locLowSpeedTimeFrac) * _localRuntimeFrac[mode];
    2908      821034 :                             runtimeFracAtSpeed[_lowSideSpeed] += _locLowSpeedRuntimeFrac;
    2909      821034 :                             runtimeFracAtSpeed[_hiSideSpeed] += _locHiSpeedRuntimeFrac;
    2910             :                         }
    2911     1634076 :                         if (_lowSideSpeed != -1 && _hiSideSpeed != -1) {
    2912      821034 :                             totalPower += max(0.0,
    2913      821034 :                                               _locLowSpeedRuntimeFrac * massFlowAtSpeed[_lowSideSpeed] * _localPressureRise[mode] /
    2914      821034 :                                                       (totalEffAtSpeed[_lowSideSpeed] * rhoAirStdInit) +
    2915      821034 :                                                   _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
    2916      821034 :                                                       (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
    2917      813042 :                         } else if (_lowSideSpeed == -1 && _hiSideSpeed == 0) {
    2918      813042 :                             totalPower += max(0.0,
    2919      813042 :                                               _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
    2920      813042 :                                                   (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
    2921             :                         }
    2922             :                     }
    2923             :                 } else {
    2924             :                     // Use localFlowFraction which is not locked at a particular flow ratio (legacy method for fan:onoff)
    2925     3519396 :                     Real64 _locLowSpeedRuntimeFrac = 0.0;
    2926     3519396 :                     Real64 _locHiSpeedRuntimeFrac = 0.0;
    2927     3519396 :                     Real64 _locRuntimeFrac = (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0)
    2928     3519396 :                                                  ? _localFlowFrac
    2929       10187 :                                                  : max(0.0, min(1.0, _localFlowFrac / state.dataHVACGlobal->OnOffFanPartLoadFraction));
    2930             : 
    2931     3519396 :                     if (numSpeeds == 1) { // CV or OnOff
    2932     3294707 :                         _localTotalEff = totalEff;
    2933     3294707 :                         _locHiSpeedRuntimeFrac = _locRuntimeFrac;
    2934     3294707 :                         runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
    2935     3294707 :                         totalPower +=
    2936     3294707 :                             max(0.0, _locHiSpeedRuntimeFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
    2937      224689 :                     } else if (numSpeeds > 1) { // multi speed
    2938             : 
    2939             :                         // find which two speed levels bracket flow fraction and calculate runtimefraction
    2940      224689 :                         int _lowSideSpeed = -1;
    2941      224689 :                         int _hiSideSpeed = -1;
    2942             : 
    2943      224689 :                         if (_locRuntimeFrac < flowFracAtSpeed[0]) { // on/off between zero and lowest speed
    2944       11823 :                             _hiSideSpeed = 0;
    2945       11823 :                             _locHiSpeedRuntimeFrac = _locRuntimeFrac / flowFracAtSpeed[0];
    2946       11823 :                             runtimeFracAtSpeed[0] += _locHiSpeedRuntimeFrac;
    2947             :                         } else {
    2948      212866 :                             _lowSideSpeed = 0; // hush up cppcheck
    2949      212866 :                             _hiSideSpeed = 0;  // hush up cppcheck
    2950      219156 :                             for (int loop = 0; loop < numSpeeds - 1; ++loop) {
    2951      219156 :                                 if ((flowFracAtSpeed[loop] <= _locRuntimeFrac) && (_locRuntimeFrac <= flowFracAtSpeed[loop + 1])) {
    2952      212866 :                                     _lowSideSpeed = loop;
    2953      212866 :                                     _hiSideSpeed = loop + 1;
    2954      212866 :                                     break;
    2955             :                                 }
    2956             :                             }
    2957      212866 :                             _locLowSpeedRuntimeFrac =
    2958      212866 :                                 (flowFracAtSpeed[_hiSideSpeed] - _locRuntimeFrac) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
    2959      212866 :                             _locHiSpeedRuntimeFrac =
    2960      212866 :                                 (_locRuntimeFrac - flowFracAtSpeed[_lowSideSpeed]) / (flowFracAtSpeed[_hiSideSpeed] - flowFracAtSpeed[_lowSideSpeed]);
    2961      212866 :                             runtimeFracAtSpeed[_lowSideSpeed] += _locLowSpeedRuntimeFrac;
    2962      212866 :                             runtimeFracAtSpeed[_hiSideSpeed] += _locHiSpeedRuntimeFrac;
    2963             :                         }
    2964      224689 :                         if (_lowSideSpeed != -1 && _hiSideSpeed != -1) {
    2965      212866 :                             totalPower += max(0.0,
    2966      212866 :                                               _locLowSpeedRuntimeFrac * massFlowAtSpeed[_lowSideSpeed] * _localPressureRise[mode] /
    2967      212866 :                                                       (totalEffAtSpeed[_lowSideSpeed] * rhoAirStdInit) +
    2968      212866 :                                                   _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
    2969      212866 :                                                       (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
    2970       11823 :                         } else if (_lowSideSpeed == -1 && _hiSideSpeed == 0) {
    2971       11823 :                             totalPower += max(0.0,
    2972       11823 :                                               _locHiSpeedRuntimeFrac * massFlowAtSpeed[_hiSideSpeed] * _localPressureRise[mode] /
    2973       11823 :                                                   (totalEffAtSpeed[_hiSideSpeed] * rhoAirStdInit));
    2974             :                         }
    2975             :                     }
    2976             :                 }
    2977     8351743 :                 _localTotalEff = totalEff;
    2978     8351743 :             } break;
    2979             : 
    2980     1455691 :             case SpeedControl::Continuous: {
    2981     1455691 :                 _localTotalEff = totalEff;
    2982     1455691 :                 Real64 _locFlowRatio(0.0);
    2983     1455691 :                 Real64 _locRuntimeFrac(0.0);
    2984     1455691 :                 if (_useFlowRatiosAndRunTimeFracs) {
    2985      831945 :                     _locFlowRatio = _localFlowRatio[mode];
    2986      831945 :                     _locRuntimeFrac = _localRuntimeFrac[mode];
    2987             :                 } else {
    2988      623746 :                     _locFlowRatio = _localFlowFrac;
    2989      623746 :                     _locRuntimeFrac = 1.0;
    2990             :                 }
    2991             : 
    2992     1455691 :                 Real64 _localFlowFracForPower = max(minPowerFlowFrac, _locFlowRatio);
    2993     1455691 :                 Real64 _localPowerFrac = (state.dataHVACGlobal->NightVentOn) ? 1.0 : // not sure why, but legacy fan had this for night ventilation
    2994     1455691 :                                              Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _localFlowFracForPower);
    2995             :                 Real64 _localFanPower =
    2996     1455691 :                     max(0.0, _locRuntimeFrac * _localPowerFrac * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit));
    2997     1455691 :                 Real64 _shaftPower = motorEff * _localFanPower;
    2998     1455691 :                 Real64 _localPowerLossToAir = _shaftPower + (_localFanPower - _shaftPower) * motorInAirFrac;
    2999     1455691 :                 outletAirEnthalpy = inletAirEnthalpy + _localPowerLossToAir / _localAirMassFlow[mode]; // this will get revised later
    3000     1455691 :                 outletAirHumRat = inletAirHumRat;                                                      // this will get revised later
    3001     1455691 :                 outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);        // this will get revised later
    3002             :                 // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
    3003             :                 //  avoid the unrealistic high temperature rise across the fan.
    3004     1455691 :                 Real64 _deltaTAcrossFan = outletAirTemp - inletAirTemp;
    3005     1455691 :                 if (_deltaTAcrossFan > 20.0) {
    3006           0 :                     Real64 _minFlowFracLimitFanHeat = 0.10;
    3007           0 :                     Real64 _powerFracAtLowMin = 0.0;
    3008           0 :                     Real64 _fanPowerAtLowMinimum = 0.0;
    3009           0 :                     if (_localFlowFracForPower < _minFlowFracLimitFanHeat) {
    3010           0 :                         _powerFracAtLowMin = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _minFlowFracLimitFanHeat);
    3011           0 :                         _fanPowerAtLowMinimum = _powerFracAtLowMin * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit);
    3012           0 :                         _localFanPower = max(0.0, _localFlowFracForPower * _fanPowerAtLowMinimum / _minFlowFracLimitFanHeat);
    3013           0 :                     } else if (_locFlowRatio < _minFlowFracLimitFanHeat) {
    3014           0 :                         _powerFracAtLowMin = Curve::CurveValue(state, powerModFuncFlowFracCurveNum, _minFlowFracLimitFanHeat);
    3015           0 :                         _fanPowerAtLowMinimum = _powerFracAtLowMin * maxAirMassFlowRate * _localPressureRise[mode] / (_localTotalEff * rhoAirStdInit);
    3016           0 :                         _localFanPower = max(0.0, _locFlowRatio * _fanPowerAtLowMinimum / _minFlowFracLimitFanHeat);
    3017             :                     }
    3018             :                 }
    3019     1455691 :                 totalPower += _localFanPower;
    3020     1455691 :             } break;
    3021             :             // continuous speed control case
    3022           0 :             case SpeedControl::Invalid: {
    3023             :                 // do nothing
    3024           0 :             } break;
    3025           0 :             default:
    3026           0 :                 assert(false);
    3027             :             } // end switch
    3028     9807434 :             outletAirMassFlowRate += _localAirMassFlow[mode];
    3029             : 
    3030             :         } // end of operating mode loop
    3031             : 
    3032     8844835 :         if (outletAirMassFlowRate > 0.0) {
    3033     8844835 :             Real64 _shaftPower = motorEff * totalPower; // power delivered to shaft
    3034     8844835 :             powerLossToAir = _shaftPower + (totalPower - _shaftPower) * motorInAirFrac;
    3035     8844835 :             outletAirEnthalpy = inletAirEnthalpy + powerLossToAir / outletAirMassFlowRate;
    3036             :             // This fan does not change the moisture or Mass Flow across the component
    3037     8844835 :             outletAirHumRat = inletAirHumRat;
    3038     8844835 :             outletAirTemp = Psychrometrics::PsyTdbFnHW(outletAirEnthalpy, outletAirHumRat);
    3039             :         } else {
    3040           0 :             totalPower = 0.0;
    3041           0 :             powerLossToAir = 0.0;
    3042           0 :             outletAirHumRat = inletAirHumRat;
    3043           0 :             outletAirEnthalpy = inletAirEnthalpy;
    3044           0 :             outletAirTemp = inletAirTemp;
    3045           0 :             massFlowRateMaxAvail = 0.0;
    3046           0 :             massFlowRateMinAvail = 0.0;
    3047             :         }
    3048             :     } else { // fan is off
    3049             :         // Fan is off and not operating no power consumed and mass flow rate.
    3050     1917882 :         totalPower = 0.0;
    3051     1917882 :         powerLossToAir = 0.0;
    3052     1917882 :         outletAirHumRat = inletAirHumRat;
    3053     1917882 :         outletAirEnthalpy = inletAirEnthalpy;
    3054     1917882 :         outletAirTemp = inletAirTemp;
    3055             :         // Set the Control Flow variables to 0.0 flow when OFF.
    3056     1917882 :         if (isSecondaryDriver) {
    3057             :             // sometimes the air is moving with the fan off, eg. AirTerminal:SingleDuct:VAV:Reheat:VariableSpeedFan
    3058       71934 :             outletAirMassFlowRate = _localAirMassFlow[0] + _localAirMassFlow[1];
    3059       71934 :             if (outletAirMassFlowRate == 0.0) {
    3060          44 :                 massFlowRateMaxAvail = 0.0;
    3061          44 :                 massFlowRateMinAvail = 0.0;
    3062             :             }
    3063             :         } else {
    3064     1845948 :             outletAirMassFlowRate = 0.0;
    3065     1845948 :             massFlowRateMaxAvail = 0.0;
    3066     1845948 :             massFlowRateMinAvail = 0.0;
    3067             :         }
    3068             :     }
    3069             : 
    3070    10762717 :     if (heatLossDest == HeatLossDest::Zone) {
    3071           0 :         Real64 _powerLossToZone = totalPower - powerLossToAir;
    3072           0 :         qdotConvZone = _powerLossToZone * (1.0 - zoneRadFract);
    3073           0 :         qdotRadZone = _powerLossToZone * zoneRadFract;
    3074             :     }
    3075    10762717 :     state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1
    3076    10762717 : } // FanSystem::report()
    3077             : 
    3078    10762717 : void FanSystem::update(EnergyPlusData &state) // does not change state of object, only update elsewhere
    3079             : {
    3080             :     // Set the outlet air node of the fan
    3081    10762717 :     state.dataLoopNodes->Node(outletNodeNum).MassFlowRate = outletAirMassFlowRate;
    3082    10762717 :     state.dataLoopNodes->Node(outletNodeNum).Temp = outletAirTemp;
    3083    10762717 :     state.dataLoopNodes->Node(outletNodeNum).HumRat = outletAirHumRat;
    3084    10762717 :     state.dataLoopNodes->Node(outletNodeNum).Enthalpy = outletAirEnthalpy;
    3085             :     // Set the outlet nodes for properties that just pass through & not used
    3086    10762717 :     state.dataLoopNodes->Node(outletNodeNum).Quality = state.dataLoopNodes->Node(inletNodeNum).Quality;
    3087    10762717 :     state.dataLoopNodes->Node(outletNodeNum).Press = state.dataLoopNodes->Node(inletNodeNum).Press;
    3088             : 
    3089             :     // Set the Node Flow Control Variables from the Fan Control Variables
    3090    10762717 :     state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMaxAvail = massFlowRateMaxAvail;
    3091    10762717 :     state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMinAvail = massFlowRateMinAvail;
    3092             : 
    3093             :     // make sure inlet has the same mass flow
    3094    10762717 :     state.dataLoopNodes->Node(inletNodeNum).MassFlowRate = outletAirMassFlowRate;
    3095             : 
    3096    10762717 :     if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    3097           0 :         state.dataLoopNodes->Node(outletNodeNum).CO2 = state.dataLoopNodes->Node(inletNodeNum).CO2;
    3098             :     }
    3099    10762717 :     if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    3100           0 :         state.dataLoopNodes->Node(outletNodeNum).GenContam = state.dataLoopNodes->Node(inletNodeNum).GenContam;
    3101             :     }
    3102             : 
    3103    10762717 :     if (isAFNFan && (airLoopNum > 0)) {
    3104      107946 :         if (speedControl == SpeedControl::Continuous) {
    3105           0 :             state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = runtimeFracAtSpeed[0];
    3106             :         } else {
    3107      107946 :             if (numSpeeds == 1) {
    3108      107946 :                 state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = outletAirMassFlowRate / maxAirMassFlowRate;
    3109           0 :             } else if (outletAirMassFlowRate <= massFlowAtSpeed[0]) {
    3110           0 :                 state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = outletAirMassFlowRate / massFlowAtSpeed[0];
    3111             :             } else {
    3112           0 :                 state.dataAirLoop->AirLoopAFNInfo(airLoopNum).AFNLoopOnOffFanRTF = 1.0;
    3113             :             }
    3114             :         }
    3115             :     }
    3116    10762717 : } // FanSystem::update()
    3117             : 
    3118    10762717 : void FanSystem::report(EnergyPlusData &state)
    3119             : {
    3120    10762717 :     totalEnergy = totalPower * state.dataHVACGlobal->TimeStepSysSec;
    3121    10762717 :     deltaTemp = outletAirTemp - inletAirTemp;
    3122    10762717 : }
    3123             : 
    3124           0 : Real64 FanSystem::getDesignTemperatureRise(EnergyPlusData &state) const
    3125             : {
    3126           0 :     if (!sizingFlag) {
    3127           0 :         Real64 _cpAir = Psychrometrics::PsyCpAirFnW(DataPrecisionGlobals::constant_zero);
    3128           0 :         return (deltaPress / (rhoAirStdInit * _cpAir * totalEff)) * (motorEff + motorInAirFrac * (1.0 - motorEff));
    3129             :     } else {
    3130             :         // TODO throw warning, exception, call sizing?
    3131           0 :         ShowWarningError(state, "FanSystem::getDesignTemperatureRise called before fan sizing completed ");
    3132           0 :         return 0.0;
    3133             :     }
    3134             : }
    3135             : 
    3136          15 : Real64 FanSystem::getDesignHeatGain(EnergyPlusData &state, Real64 const _volFlow // fan volume flow rate [m3/s]
    3137             : )
    3138             : {
    3139          15 :     if (sizingFlag) {
    3140           0 :         set_size(state);
    3141             :     }
    3142             : 
    3143          15 :     Real64 _fanPowerTot = (_volFlow * deltaPress) / totalEff;
    3144          15 :     return motorEff * _fanPowerTot + (_fanPowerTot - motorEff * _fanPowerTot) * motorInAirFrac;
    3145             : }
    3146             : 
    3147        1989 : void FanSystem::getInputsForDesignHeatGain(EnergyPlusData &state,
    3148             :                                            Real64 &_deltaP,
    3149             :                                            Real64 &_motEff,
    3150             :                                            Real64 &_totEff,
    3151             :                                            Real64 &_motInAirFrac,
    3152             :                                            Real64 &_shaftPower,
    3153             :                                            Real64 &_motInPower,
    3154             :                                            bool &_fanComponentModel)
    3155             : {
    3156        1989 :     if (sizingFlag) {
    3157         156 :         set_size(state);
    3158             :     }
    3159             : 
    3160        1989 :     _deltaP = deltaPress;
    3161        1989 :     _motEff = motorEff;
    3162        1989 :     _totEff = totalEff;
    3163        1989 :     _motInAirFrac = motorInAirFrac;
    3164             : 
    3165        1989 :     _shaftPower = 0.0;
    3166        1989 :     _motInPower = 0.0;
    3167        1989 :     _fanComponentModel = false;
    3168        1989 : }
    3169             : } // namespace EnergyPlus::Fans

Generated by: LCOV version 1.14