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

Generated by: LCOV version 2.0-1