LCOV - code coverage report
Current view: top level - EnergyPlus - Fans.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 62.7 % 1631 1023
Test Date: 2025-06-02 12:03:30 Functions: 95.8 % 24 23

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

Generated by: LCOV version 2.0-1