LCOV - code coverage report
Current view: top level - EnergyPlus - HVACFan.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 520 655 79.4 %
Date: 2023-01-17 19:17:23 Functions: 16 18 88.9 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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             : // EnergyPlus Headers
      49             : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.hh>
      50             : #include <EnergyPlus/BranchNodeConnections.hh>
      51             : #include <EnergyPlus/CurveManager.hh>
      52             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      53             : #include <EnergyPlus/DataContaminantBalance.hh>
      54             : #include <EnergyPlus/DataEnvironment.hh>
      55             : #include <EnergyPlus/DataHVACGlobals.hh>
      56             : #include <EnergyPlus/DataHeatBalance.hh>
      57             : #include <EnergyPlus/DataLoopNode.hh>
      58             : #include <EnergyPlus/DataPrecisionGlobals.hh>
      59             : #include <EnergyPlus/DataSizing.hh>
      60             : #include <EnergyPlus/EMSManager.hh>
      61             : #include <EnergyPlus/EnergyPlus.hh>
      62             : #include <EnergyPlus/Fans.hh>
      63             : #include <EnergyPlus/FaultsManager.hh>
      64             : #include <EnergyPlus/HVACFan.hh>
      65             : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
      66             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      67             : #include <EnergyPlus/NodeInputManager.hh>
      68             : #include <EnergyPlus/OutputProcessor.hh>
      69             : #include <EnergyPlus/OutputReportPredefined.hh>
      70             : #include <EnergyPlus/Psychrometrics.hh>
      71             : #include <EnergyPlus/ScheduleManager.hh>
      72             : #include <ObjexxFCL/Optional.hh>
      73             : 
      74             : namespace EnergyPlus {
      75             : 
      76             : namespace HVACFan {
      77             : 
      78         269 :     int getFanObjectVectorIndex( // lookup vector index for fan object name in object array EnergyPlus::HVACFan::fanObjs
      79             :         EnergyPlusData &state,
      80             :         std::string const &objectName, // IDF name in input
      81             :         bool const ErrorCheck)
      82             :     {
      83         269 :         int index = -1;
      84         269 :         bool found = false;
      85         907 :         for (std::size_t loop = 0; loop < state.dataHVACFan->fanObjs.size(); ++loop) {
      86         638 :             if (objectName == state.dataHVACFan->fanObjs[loop]->name) {
      87         204 :                 if (!found) {
      88         204 :                     index = loop;
      89         204 :                     found = true;
      90             :                 } else { // found duplicate
      91             :                     // TODO throw warning?
      92           0 :                     index = -1;
      93           0 :                     ShowSevereError(state,
      94           0 :                                     "getFanObjectVectorIndex: Found duplicate Fan:SystemModel inputs of name =" + objectName + ". Check inputs");
      95             :                 }
      96             :             }
      97             :         }
      98         269 :         if (!found && ErrorCheck) {
      99           0 :             ShowSevereError(state, "getFanObjectVectorIndex: did not find Fan:SystemModel name =" + objectName + ". Check inputs");
     100             :         }
     101         269 :         return index;
     102             :     }
     103             : 
     104         351 :     bool checkIfFanNameIsAFanSystem( // look up to see if input contains a Fan:SystemModel with the name (for use before object construction
     105             :         EnergyPlusData &state,
     106             :         std::string const &objectName)
     107             :     {
     108             : 
     109         351 :         int testNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "Fan:SystemModel", objectName);
     110         351 :         if (testNum > 0) {
     111          99 :             return true;
     112             :         } else {
     113         252 :             return false;
     114             :         }
     115             :     }
     116             : 
     117     7213445 :     void FanSystem::simulate(
     118             :         EnergyPlusData &state,
     119             :         Optional<Real64 const> flowFraction, // when used, this directs the fan to set the flow at this flow fraction = current flow/ max design flow
     120             :                                              // rate.  It is not exactly the same as the legacy speed ratio that was used with SimulateFanComponents.
     121             :         Optional_bool_const zoneCompTurnFansOn,  // can be used as turn fans ON signal from ZoneHVAC component
     122             :         Optional_bool_const zoneCompTurnFansOff, // can be used as turn Fans OFF signal from ZoneHVAC component
     123             :         Optional<Real64 const>
     124             :             pressureRise, // Pressure difference to use for DeltaPress, for rating DX coils at a different pressure without entire duct system
     125             :         Optional<Real64 const> massFlowRate1,    // Mass flow rate in operating mode 1 [kg/s]
     126             :         Optional<Real64 const> runTimeFraction1, // Run time fraction in operating mode 1
     127             :         Optional<Real64 const> massFlowRate2,    // Mass flow rate in operating mode 2 [kg/s]
     128             :         Optional<Real64 const> runTimeFraction2, // Run time fraction in opearating mode 2
     129             :         Optional<Real64 const> pressureRise2     // Pressure difference for operating mode 2
     130             :     )
     131             :     {
     132             : 
     133     7213445 :         m_objTurnFansOn = false;
     134     7213445 :         m_objTurnFansOff = false;
     135             : 
     136     7213445 :         init(state);
     137             : 
     138     7213445 :         if (m_objSizingFlag) {
     139         227 :             return; // can't run calculations until sizing is completed
     140             :         }
     141             : 
     142     7213218 :         if (present(zoneCompTurnFansOn) && present(zoneCompTurnFansOff)) {
     143             :             // Set module-level logic flags equal to ZoneCompTurnFansOn and ZoneCompTurnFansOff values passed into this routine
     144             :             // for ZoneHVAC components with system availability managers defined.
     145             :             // The module-level flags get used in the other subroutines (e.g., SimSimpleFan,SimVariableVolumeFan and SimOnOffFan)
     146     2727053 :             m_objTurnFansOn = zoneCompTurnFansOn;
     147     2727053 :             m_objTurnFansOff = zoneCompTurnFansOff;
     148             :         } else {
     149             :             // Set module-level logic flags equal to the global LocalTurnFansOn and LocalTurnFansOff variables for all other cases.
     150     4486165 :             m_objTurnFansOn = state.dataHVACGlobal->TurnFansOn;
     151     4486165 :             m_objTurnFansOff = state.dataHVACGlobal->TurnFansOff;
     152             :         }
     153     7213218 :         if (present(pressureRise) && present(massFlowRate1) && present(runTimeFraction1) && present(massFlowRate2) && present(runTimeFraction2) &&
     154           0 :             present(pressureRise2)) {
     155           0 :             Real64 flowRatio1 = massFlowRate1 / m_maxAirMassFlowRate;
     156           0 :             Real64 flowRatio2 = massFlowRate2 / m_maxAirMassFlowRate;
     157           0 :             calcSimpleSystemFan(state, _, pressureRise, flowRatio1, runTimeFraction1, flowRatio2, runTimeFraction2, pressureRise2);
     158    24643104 :         } else if (!present(pressureRise) && present(massFlowRate1) && present(runTimeFraction1) && present(massFlowRate2) &&
     159    13229374 :                    present(runTimeFraction2) && !present(pressureRise2)) {
     160     3008078 :             Real64 flowRatio1 = massFlowRate1 / m_maxAirMassFlowRate;
     161     3008078 :             Real64 flowRatio2 = massFlowRate2 / m_maxAirMassFlowRate;
     162     3008078 :             calcSimpleSystemFan(state, flowFraction, _, flowRatio1, runTimeFraction1, flowRatio2, runTimeFraction2, _);
     163     4205140 :         } else if (present(pressureRise) && present(flowFraction)) {
     164           0 :             calcSimpleSystemFan(state, flowFraction, pressureRise, _, _, _, _, _);
     165     4205140 :         } else if (present(pressureRise) && !present(flowFraction)) {
     166        4628 :             calcSimpleSystemFan(state, _, pressureRise, _, _, _, _, _);
     167     4200512 :         } else if (!present(pressureRise) && present(flowFraction)) {
     168      936396 :             calcSimpleSystemFan(state, flowFraction, _, _, _, _, _, _);
     169             :         } else {
     170     3264116 :             calcSimpleSystemFan(state, _, _, _, _, _, _, _);
     171             :         }
     172             : 
     173     7213218 :         update(state);
     174             : 
     175     7213218 :         report(state);
     176             :     }
     177             : 
     178     7213445 :     void FanSystem::init(EnergyPlusData &state)
     179             :     {
     180     7213445 :         if (!state.dataGlobal->SysSizingCalc && m_objSizingFlag) {
     181          54 :             set_size(state);
     182          54 :             m_objSizingFlag = false;
     183             :         }
     184             : 
     185     7213445 :         if (state.dataGlobal->BeginEnvrnFlag && m_objEnvrnFlag) {
     186             : 
     187             :             // Currently, fan does not force minimum mass flow, only used for power calculation
     188             :             // m_minAirFlowRate = designAirVolFlowRate * m_minPowerFlowFrac;
     189             :             // m_minAirMassFlowRate = m_minAirFlowRate * m_rhoAirStdInit;
     190             : 
     191             :             // Init the Node Control variables
     192        1033 :             state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax = m_maxAirMassFlowRate;
     193             :             // Currently, fan does not force minimum mass flow, only used for power calculation
     194             :             // DataLoopNode::Node( outletNodeNum ).MassFlowRateMin = m_minAirMassFlowRate;
     195             : 
     196             :             // Initialize all report variables to a known state at beginning of simulation
     197        1033 :             m_fanPower = 0.0;
     198        1033 :             m_deltaTemp = 0.0;
     199        1033 :             m_powerLossToAir = 0.0;
     200        1033 :             m_fanEnergy = 0.0;
     201        2221 :             for (auto loop = 0; loop < m_numSpeeds; ++loop) {
     202        1188 :                 m_fanRunTimeFractionAtSpeed[loop] = 0.0;
     203             :             }
     204        1033 :             m_objEnvrnFlag = false;
     205             :         }
     206             : 
     207     7213445 :         if (!state.dataGlobal->BeginEnvrnFlag) {
     208     7178987 :             m_objEnvrnFlag = true;
     209             :         }
     210             : 
     211     7213445 :         m_massFlowRateMaxAvail =
     212     7213445 :             min(state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMax, state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMaxAvail);
     213     7213445 :         m_massFlowRateMinAvail =
     214     7213445 :             min(max(state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMin, state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMinAvail),
     215     7213445 :                 state.dataLoopNodes->Node(inletNodeNum).MassFlowRateMaxAvail);
     216             : 
     217             :         // Load the node data in this section for the component simulation
     218             :         // First need to make sure that the MassFlowRate is between the max and min avail.
     219     7213445 :         m_inletAirMassFlowRate = min(state.dataLoopNodes->Node(inletNodeNum).MassFlowRate, m_massFlowRateMaxAvail);
     220     7213445 :         m_inletAirMassFlowRate = max(m_inletAirMassFlowRate, m_massFlowRateMinAvail);
     221             : 
     222             :         // Then set the other conditions
     223     7213445 :         m_inletAirTemp = state.dataLoopNodes->Node(inletNodeNum).Temp;
     224     7213445 :         m_inletAirHumRat = state.dataLoopNodes->Node(inletNodeNum).HumRat;
     225     7213445 :         m_inletAirEnthalpy = state.dataLoopNodes->Node(inletNodeNum).Enthalpy;
     226     7213445 :     }
     227             : 
     228         171 :     void FanSystem::set_size(EnergyPlusData &state)
     229             :     {
     230             :         static constexpr std::string_view routineName = "FanSystem::set_size ";
     231             : 
     232         171 :         Real64 tempFlow = designAirVolFlowRate;
     233         171 :         bool bPRINT = true;
     234         171 :         state.dataSize->DataAutosizable = true;
     235         171 :         state.dataSize->DataEMSOverrideON = m_maxAirFlowRateEMSOverrideOn;
     236         171 :         state.dataSize->DataEMSOverride = m_maxAirFlowRateEMSOverrideValue;
     237             : 
     238         171 :         bool errorsFound = false;
     239         342 :         SystemAirFlowSizer sizerSystemAirFlow;
     240         171 :         sizerSystemAirFlow.initializeWithinEP(state, m_fanType, name, bPRINT, routineName);
     241         171 :         designAirVolFlowRate = sizerSystemAirFlow.size(state, tempFlow, errorsFound);
     242             : 
     243         171 :         state.dataSize->DataAutosizable = true; // should be false?
     244         171 :         state.dataSize->DataEMSOverrideON = false;
     245         171 :         state.dataSize->DataEMSOverride = 0.0;
     246             : 
     247         171 :         if (m_designElecPowerWasAutosized) {
     248             : 
     249         170 :             switch (m_powerSizingMethod) {
     250             : 
     251           0 :             case PowerSizingMethod::PowerPerFlow: {
     252           0 :                 designElecPower = designAirVolFlowRate * m_elecPowerPerFlowRate;
     253           0 :                 break;
     254             :             }
     255           2 :             case PowerSizingMethod::PowerPerFlowPerPressure: {
     256           2 :                 designElecPower = designAirVolFlowRate * deltaPress * m_elecPowerPerFlowRatePerPressure;
     257           2 :                 break;
     258             :             }
     259         168 :             case PowerSizingMethod::TotalEfficiencyAndPressure: {
     260         168 :                 designElecPower = designAirVolFlowRate * deltaPress / m_fanTotalEff;
     261         168 :                 break;
     262             :             }
     263           0 :             case PowerSizingMethod::Invalid: {
     264             :                 // do nothing
     265           0 :                 break;
     266             :             }
     267           0 :             default:
     268           0 :                 assert(false);
     269             : 
     270             :             } // end switch
     271             : 
     272             :             // report design power
     273         170 :             BaseSizer::reportSizerOutput(state, m_fanType, name, "Design Electric Power Consumption [W]", designElecPower);
     274             : 
     275             :         } // end if power was autosized
     276             : 
     277         171 :         m_rhoAirStdInit = state.dataEnvrn->StdRhoAir;
     278         171 :         m_maxAirMassFlowRate = designAirVolFlowRate * m_rhoAirStdInit;
     279             : 
     280             :         // calculate total fan system efficiency at design, else set to 1 to avoid div by zero
     281         171 :         if (designElecPower > 0.0) {
     282         171 :             m_fanTotalEff = designAirVolFlowRate * deltaPress / designElecPower;
     283             :         } else {
     284           0 :             m_fanTotalEff = 1.0;
     285             :         }
     286             : 
     287         171 :         if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) { // set up values at speeds
     288          20 :             m_massFlowAtSpeed.resize(m_numSpeeds, 0.0);
     289          20 :             m_totEfficAtSpeed.resize(m_numSpeeds, 0.0);
     290          63 :             for (auto loop = 0; loop < m_numSpeeds; ++loop) {
     291          43 :                 m_massFlowAtSpeed[loop] = m_maxAirMassFlowRate * m_flowFractionAtSpeed[loop];
     292          43 :                 if (m_powerFractionInputAtSpeed[loop]) { // use speed power fraction
     293          43 :                     if (designElecPower > 0.0) {
     294          43 :                         m_totEfficAtSpeed[loop] =
     295          43 :                             m_flowFractionAtSpeed[loop] * designAirVolFlowRate * deltaPress / (designElecPower * m_powerFractionAtSpeed[loop]);
     296             :                     } else {
     297           0 :                         m_totEfficAtSpeed[loop] = 1.0;
     298             :                     }
     299             :                 } else { // use power curve
     300           0 :                     m_totEfficAtSpeed[loop] =
     301           0 :                         m_flowFractionAtSpeed[loop] * designAirVolFlowRate * deltaPress /
     302           0 :                         (designElecPower * Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, m_flowFractionAtSpeed[loop]));
     303           0 :                     m_powerFractionAtSpeed[loop] = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, m_flowFractionAtSpeed[loop]);
     304             :                 }
     305             :             }
     306             :         }
     307         171 :         Real64 rhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataLoopNodes->Node(inletNodeNum).Press, m_inletAirTemp, m_inletAirHumRat);
     308         171 :         m_designPointFEI = report_fei(state, designAirVolFlowRate, designElecPower, deltaPress, rhoAir);
     309             : 
     310         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanType, name, m_fanType);
     311         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanTotEff, name, m_fanTotalEff);
     312         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanDeltaP, name, deltaPress);
     313         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanVolFlow, name, designAirVolFlowRate);
     314             : 
     315         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanPwr, name, designElecPower);
     316         171 :         if (designAirVolFlowRate != 0.0) {
     317         513 :             OutputReportPredefined::PreDefTableEntry(
     318         342 :                 state, state.dataOutRptPredefined->pdchFanPwrPerFlow, name, designElecPower / designAirVolFlowRate);
     319             :         }
     320         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanMotorIn, name, m_motorInAirFrac);
     321         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEnergyIndex, name, m_designPointFEI);
     322             : 
     323         171 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchFanEndUse, name, m_endUseSubcategoryName);
     324             : 
     325         171 :         m_objSizingFlag = false;
     326         171 :     }
     327             : 
     328        2110 :     Real64 FanSystem::report_fei(
     329             :         EnergyPlusData &state, Real64 const designFlowRate, Real64 const designElecPower, Real64 const designDeltaPress, Real64 inletRhoAir)
     330             :     {
     331             :         // PURPOSE OF THIS SUBROUTINE:
     332             :         // Calculate the Fan Energy Index
     333             : 
     334             :         // REFERENCES:
     335             :         // ANSI/AMCA Standard 207-17: Fan System Efficiency and Fan System Input Power Calculation, 2017.
     336             :         // AANSI / AMCA Standard 208 - 18: Calculation of the Fan Energy Index, 2018.
     337             : 
     338        2110 :         assert(state.dataEnvrn->StdRhoAir > 0.0);
     339             :         // Calculate reference fan shaft power
     340        2110 :         Real64 refFanShaftPower = (designFlowRate + 0.118) * (designDeltaPress + 100 * inletRhoAir / state.dataEnvrn->StdRhoAir) / (1000 * 0.66);
     341             : 
     342             :         // Calculate reference reference fan transmission efficiency
     343        2110 :         Real64 refFanTransEff = 0.96 * pow((refFanShaftPower / (refFanShaftPower + 1.64)), 0.05);
     344             : 
     345             :         // Calculate reference reference fan motor efficiency
     346        2110 :         Real64 refFanMotorOutput = refFanShaftPower / refFanTransEff;
     347             : 
     348             :         Real64 refFanMotorEff;
     349        2110 :         if (refFanMotorOutput < 185.0) {
     350        6237 :             refFanMotorEff = -0.003812 * pow(std::log10(refFanMotorOutput), 4) + 0.025834 * pow(std::log10(refFanMotorOutput), 3) -
     351        4158 :                              0.072577 * pow(std::log10(refFanMotorOutput), 2) + 0.125559 * std::log10(refFanMotorOutput) + 0.850274;
     352             :         } else {
     353          31 :             refFanMotorEff = 0.962;
     354             :         }
     355             : 
     356             :         // Calculate reference reference fan motor controller  efficiency
     357        2110 :         Real64 refFanMotorCtrlEff = 1;
     358             : 
     359        2110 :         Real64 refFanElecPower = refFanShaftPower / (refFanTransEff * refFanMotorEff * refFanMotorCtrlEff);
     360             : 
     361        2110 :         if (designElecPower > 0.0) {
     362        2086 :             return refFanElecPower * 1000 / designElecPower;
     363             :         } else {
     364          24 :             return 0.0;
     365             :         }
     366             :     }
     367             : 
     368         171 :     FanSystem::FanSystem(EnergyPlusData &state, std::string const &objectName)
     369             :         : availSchedIndex(0), inletNodeNum(0), outletNodeNum(0), designAirVolFlowRate(0.0), speedControl(SpeedControlMethod::NotSet), deltaPress(0.0),
     370             :           designElecPower(0.0), powerModFuncFlowFractionCurveIndex(0), AirLoopNum(0), AirPathFlag(false), fanIsSecondaryDriver(false),
     371             :           m_fanType_Num(0), m_designAirVolFlowRateWasAutosized(false), m_minPowerFlowFrac(0.0), m_motorEff(0.0), m_motorInAirFrac(0.0),
     372             :           m_designElecPowerWasAutosized(false), m_powerSizingMethod(PowerSizingMethod::Invalid), m_elecPowerPerFlowRate(0.0),
     373             :           m_elecPowerPerFlowRatePerPressure(0.0), m_fanTotalEff(0.0), m_nightVentPressureDelta(0.0), m_nightVentFlowFraction(0.0), m_zoneNum(0),
     374             :           m_zoneRadFract(0.0), m_heatLossesDestination(ThermalLossDestination::Invalid), m_qdotConvZone(0.0), m_qdotRadZone(0.0), m_numSpeeds(0),
     375             :           m_inletAirMassFlowRate(0.0), m_outletAirMassFlowRate(0.0), m_maxAirMassFlowRate(0.0), m_inletAirTemp(0.0), m_outletAirTemp(0.0),
     376             :           m_inletAirHumRat(0.0), m_outletAirHumRat(0.0), m_inletAirEnthalpy(0.0), m_outletAirEnthalpy(0.0), m_objTurnFansOn(false),
     377             :           m_objTurnFansOff(false), m_objEnvrnFlag(true), m_objSizingFlag(true), m_fanPower(0.0), m_fanEnergy(0.0),
     378             :           m_maxAirFlowRateEMSOverrideOn(false), m_maxAirFlowRateEMSOverrideValue(0.0), m_eMSFanPressureOverrideOn(false), m_eMSFanPressureValue(0.0),
     379             :           m_eMSFanEffOverrideOn(false), m_eMSFanEffValue(0.0), m_eMSMaxMassFlowOverrideOn(false), m_eMSAirMassFlowValue(0.0),
     380             :           m_faultyFilterFlag(false), m_faultyFilterIndex(0),
     381             : 
     382         171 :           m_massFlowRateMaxAvail(0.0), m_massFlowRateMinAvail(0.0), m_rhoAirStdInit(0.0), m_designPointFEI(0.0)
     383             :     // oneTimePowerCurveCheck_( true )
     384             :     {
     385             : 
     386             :         static constexpr std::string_view routineName = "HVACFan constructor ";
     387             :         int numAlphas;    // Number of elements in the alpha array
     388             :         int numNums;      // Number of elements in the numeric array
     389             :         int numTotFields; // Total number of alpha and numeric fields
     390             :         int IOStat;       // IO Status when calling get input subroutine
     391         171 :         bool errorsFound = false;
     392         342 :         std::string locCurrentModuleObject = "Fan:SystemModel";
     393         342 :         Array1D_string alphaArgs;
     394         342 :         Array1D_string alphaFieldNames;
     395         342 :         Array1D_bool isAlphaFieldBlank;
     396         342 :         Array1D<Real64> numericArgs;
     397         342 :         Array1D_string numericFieldNames;
     398         342 :         Array1D_bool isNumericFieldBlank;
     399         171 :         int objectNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, locCurrentModuleObject, objectName);
     400         171 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, locCurrentModuleObject, numTotFields, numAlphas, numNums);
     401         171 :         if (numAlphas > 0) {
     402         171 :             alphaArgs.allocate(numAlphas);
     403         171 :             alphaFieldNames.allocate(numAlphas);
     404         171 :             isAlphaFieldBlank.allocate(numAlphas);
     405             :         }
     406         171 :         if (numNums > 0) {
     407         171 :             numericArgs.allocate(numNums);
     408         171 :             numericFieldNames.allocate(numNums);
     409         171 :             isNumericFieldBlank.allocate(numNums);
     410             :         }
     411             : 
     412         171 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     413             :                                                                  locCurrentModuleObject,
     414             :                                                                  objectNum,
     415             :                                                                  alphaArgs,
     416             :                                                                  numAlphas,
     417             :                                                                  numericArgs,
     418             :                                                                  numNums,
     419             :                                                                  IOStat,
     420             :                                                                  isNumericFieldBlank,
     421             :                                                                  isAlphaFieldBlank,
     422             :                                                                  alphaFieldNames,
     423             :                                                                  numericFieldNames);
     424             : 
     425         171 :         name = alphaArgs(1);
     426             :         // TODO how to check for unique names across objects during get input?
     427         171 :         m_fanType = locCurrentModuleObject;
     428         171 :         m_fanType_Num = DataHVACGlobals::FanType_SystemModelObject;
     429         171 :         if (isAlphaFieldBlank(2)) {
     430           2 :             availSchedIndex = DataGlobalConstants::ScheduleAlwaysOn;
     431             :         } else {
     432         169 :             availSchedIndex = ScheduleManager::GetScheduleIndex(state, alphaArgs(2));
     433         169 :             if (availSchedIndex == 0) {
     434           0 :                 ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     435           0 :                 ShowContinueError(state, "Invalid " + alphaFieldNames(2) + " = " + alphaArgs(2));
     436           0 :                 errorsFound = true;
     437             :             }
     438             :         }
     439         171 :         inletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     440         171 :                                                            alphaArgs(3),
     441             :                                                            errorsFound,
     442             :                                                            DataLoopNode::ConnectionObjectType::FanSystemModel,
     443         171 :                                                            alphaArgs(1),
     444             :                                                            DataLoopNode::NodeFluidType::Air,
     445             :                                                            DataLoopNode::ConnectionType::Inlet,
     446             :                                                            NodeInputManager::CompFluidStream::Primary,
     447         171 :                                                            DataLoopNode::ObjectIsNotParent);
     448         171 :         outletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     449         171 :                                                             alphaArgs(4),
     450             :                                                             errorsFound,
     451             :                                                             DataLoopNode::ConnectionObjectType::FanSystemModel,
     452         171 :                                                             alphaArgs(1),
     453             :                                                             DataLoopNode::NodeFluidType::Air,
     454             :                                                             DataLoopNode::ConnectionType::Outlet,
     455             :                                                             NodeInputManager::CompFluidStream::Primary,
     456         171 :                                                             DataLoopNode::ObjectIsNotParent);
     457             : 
     458         171 :         BranchNodeConnections::TestCompSet(state, locCurrentModuleObject, alphaArgs(1), alphaArgs(3), alphaArgs(4), "Air Nodes");
     459             : 
     460         171 :         designAirVolFlowRate = numericArgs(1);
     461         171 :         if (designAirVolFlowRate == DataSizing::AutoSize) {
     462         126 :             m_designAirVolFlowRateWasAutosized = true;
     463             :         }
     464             : 
     465         171 :         if (isAlphaFieldBlank(5)) {
     466           0 :             speedControl = SpeedControlMethod::Discrete;
     467         171 :         } else if (UtilityRoutines::SameString(alphaArgs(5), "Continuous")) {
     468          21 :             speedControl = SpeedControlMethod::Continuous;
     469         150 :         } else if (UtilityRoutines::SameString(alphaArgs(5), "Discrete")) {
     470         150 :             speedControl = SpeedControlMethod::Discrete;
     471             :         } else {
     472           0 :             ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     473           0 :             ShowContinueError(state, "Invalid " + alphaFieldNames(5) + " = " + alphaArgs(5));
     474           0 :             errorsFound = true;
     475             :         }
     476             : 
     477         171 :         m_minPowerFlowFrac = numericArgs(2);
     478         171 :         deltaPress = numericArgs(3);
     479         171 :         if (deltaPress <= 0.0) {
     480           0 :             ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + " zero or negative, invalid entry in " + numericFieldNames(3));
     481           0 :             errorsFound = true;
     482             :         }
     483         171 :         m_motorEff = numericArgs(4);
     484         171 :         m_motorInAirFrac = numericArgs(5);
     485         171 :         designElecPower = numericArgs(6);
     486         171 :         if (designElecPower == DataSizing::AutoSize) {
     487         170 :             m_designElecPowerWasAutosized = true;
     488             :         }
     489         171 :         if (m_designElecPowerWasAutosized) {
     490         170 :             if (isAlphaFieldBlank(6)) {
     491           0 :                 m_powerSizingMethod = PowerSizingMethod::PowerPerFlowPerPressure;
     492         170 :             } else if (UtilityRoutines::SameString(alphaArgs(6), "PowerPerFlow")) {
     493           0 :                 m_powerSizingMethod = PowerSizingMethod::PowerPerFlow;
     494         170 :             } else if (UtilityRoutines::SameString(alphaArgs(6), "PowerPerFlowPerPressure")) {
     495           2 :                 m_powerSizingMethod = PowerSizingMethod::PowerPerFlowPerPressure;
     496         168 :             } else if (UtilityRoutines::SameString(alphaArgs(6), "TotalEfficiencyAndPressure")) {
     497         168 :                 m_powerSizingMethod = PowerSizingMethod::TotalEfficiencyAndPressure;
     498             :             } else {
     499           0 :                 ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     500           0 :                 ShowContinueError(state, "Invalid " + alphaFieldNames(6) + " = " + alphaArgs(6));
     501           0 :                 errorsFound = true;
     502             :             }
     503         170 :             m_elecPowerPerFlowRate = numericArgs(7);
     504         170 :             m_elecPowerPerFlowRatePerPressure = numericArgs(8);
     505         170 :             m_fanTotalEff = numericArgs(9);
     506             :         }
     507         171 :         if (!isAlphaFieldBlank(7)) {
     508          21 :             powerModFuncFlowFractionCurveIndex = Curve::GetCurveIndex(state, alphaArgs(7));
     509          21 :             if (powerModFuncFlowFractionCurveIndex == 0) {
     510           0 :                 ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     511           0 :                 ShowContinueError(state, "Invalid " + alphaFieldNames(7) + " = " + alphaArgs(7));
     512           0 :                 ShowContinueError(state, "Curve not found.");
     513           0 :                 if (speedControl == SpeedControlMethod::Continuous) {
     514           0 :                     errorsFound = true;
     515             :                 }
     516             :             }
     517             :         } else { // blank
     518         150 :             if (speedControl == SpeedControlMethod::Continuous) {
     519           0 :                 ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     520           0 :                 ShowContinueError(state, "Continuous speed control requires a fan power curve in " + alphaFieldNames(7) + " = " + alphaArgs(7));
     521           0 :                 errorsFound = true;
     522             :             }
     523             :         }
     524         171 :         m_nightVentPressureDelta = numericArgs(10);
     525         171 :         m_nightVentFlowFraction = numericArgs(11); // not used
     526         171 :         m_zoneNum = UtilityRoutines::FindItemInList(alphaArgs(8), state.dataHeatBal->Zone);
     527         171 :         if (m_zoneNum > 0) m_heatLossesDestination = ThermalLossDestination::ZoneGains;
     528         171 :         if (m_zoneNum == 0) {
     529         171 :             if (isAlphaFieldBlank(8)) {
     530         171 :                 m_heatLossesDestination = ThermalLossDestination::LostToOutside;
     531             :             } else {
     532           0 :                 m_heatLossesDestination = ThermalLossDestination::LostToOutside;
     533           0 :                 ShowWarningError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     534           0 :                 ShowContinueError(state, "Invalid " + alphaFieldNames(8) + " = " + alphaArgs(8));
     535           0 :                 ShowContinueError(state, "Zone name not found. Fan motor heat losses will not be added to a zone");
     536             :                 // continue with simulation but motor losses not sent to a zone.
     537             :             }
     538             :         }
     539         171 :         m_zoneRadFract = numericArgs(12);
     540         171 :         if (!isAlphaFieldBlank(9)) {
     541          72 :             m_endUseSubcategoryName = alphaArgs(9);
     542             :         } else {
     543          99 :             m_endUseSubcategoryName = "General";
     544             :         }
     545             : 
     546         171 :         if (!isNumericFieldBlank(13)) {
     547          49 :             m_numSpeeds = numericArgs(13);
     548             :         } else {
     549         122 :             m_numSpeeds = 1;
     550             :         }
     551         171 :         m_fanRunTimeFractionAtSpeed.resize(m_numSpeeds, 0.0);
     552         171 :         if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) {
     553             :             // should have field sets
     554          20 :             m_flowFractionAtSpeed.resize(m_numSpeeds, 0.0);
     555          20 :             m_powerFractionAtSpeed.resize(m_numSpeeds, 0.0);
     556          20 :             m_powerFractionInputAtSpeed.resize(m_numSpeeds, false);
     557          20 :             if (m_numSpeeds == ((numNums - 13) / 2) || m_numSpeeds == ((numNums + 1 - 13) / 2)) {
     558          63 :                 for (auto loopSet = 0; loopSet < m_numSpeeds; ++loopSet) {
     559          43 :                     m_flowFractionAtSpeed[loopSet] = numericArgs(13 + loopSet * 2 + 1);
     560          43 :                     if (!isNumericFieldBlank(13 + loopSet * 2 + 2)) {
     561          43 :                         m_powerFractionAtSpeed[loopSet] = numericArgs(13 + loopSet * 2 + 2);
     562          43 :                         m_powerFractionInputAtSpeed[loopSet] = true;
     563             :                     } else {
     564           0 :                         m_powerFractionInputAtSpeed[loopSet] = false;
     565             :                     }
     566          20 :                 }
     567             :             } else {
     568             :                 // field set input does not match number of speeds, throw warning
     569           0 :                 ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     570           0 :                 ShowContinueError(state, "Fan with Discrete speed control does not have input for speed data that matches the number of speeds.");
     571           0 :                 errorsFound = true;
     572             :             }
     573             :             // check that flow fractions are increasing
     574          20 :             bool increasingOrderError = false;
     575          43 :             for (auto loop = 0; loop < (m_numSpeeds - 1); ++loop) {
     576          23 :                 if (m_flowFractionAtSpeed[loop] > m_flowFractionAtSpeed[loop + 1]) {
     577           0 :                     increasingOrderError = true;
     578             :                 }
     579             :             }
     580          20 :             if (increasingOrderError) {
     581           0 :                 ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     582           0 :                 ShowContinueError(state,
     583             :                                   "Fan with Discrete speed control and multiple speed levels does not have input with flow fractions arranged in "
     584             :                                   "increasing order.");
     585           0 :                 errorsFound = true;
     586             :             }
     587             :         }
     588             : 
     589             :         // check if power curve present when any speeds have no power fraction
     590         171 :         if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1 && powerModFuncFlowFractionCurveIndex == 0) {
     591          20 :             bool foundMissingPowerFraction = false;
     592          63 :             for (auto loop = 0; loop < m_numSpeeds; ++loop) {
     593          43 :                 if (!m_powerFractionInputAtSpeed[loop]) {
     594           0 :                     foundMissingPowerFraction = true;
     595             :                 }
     596             :             }
     597          20 :             if (foundMissingPowerFraction) {
     598             :                 // field set input does not match number of speeds, throw warning
     599           0 :                 ShowSevereError(state, std::string{routineName} + locCurrentModuleObject + "=\"" + alphaArgs(1) + "\", invalid entry.");
     600           0 :                 ShowContinueError(
     601             :                     state,
     602             :                     "Fan with Discrete speed control does not have input for power fraction at all speed levels and does not have a power curve.");
     603           0 :                 errorsFound = true;
     604             :             }
     605             :         }
     606             : 
     607         171 :         if (errorsFound) {
     608           0 :             ShowFatalError(state, std::string{routineName} + "Errors found in input for fan name = " + name + ".  Program terminates.");
     609             :         }
     610             : 
     611         342 :         SetupOutputVariable(state,
     612             :                             "Fan Electricity Rate",
     613             :                             OutputProcessor::Unit::W,
     614             :                             m_fanPower,
     615             :                             OutputProcessor::SOVTimeStepType::System,
     616             :                             OutputProcessor::SOVStoreType::Average,
     617         171 :                             name);
     618         342 :         SetupOutputVariable(state,
     619             :                             "Fan Rise in Air Temperature",
     620             :                             OutputProcessor::Unit::deltaC,
     621             :                             m_deltaTemp,
     622             :                             OutputProcessor::SOVTimeStepType::System,
     623             :                             OutputProcessor::SOVStoreType::Average,
     624         171 :                             name);
     625         342 :         SetupOutputVariable(state,
     626             :                             "Fan Heat Gain to Air",
     627             :                             OutputProcessor::Unit::W,
     628             :                             m_powerLossToAir,
     629             :                             OutputProcessor::SOVTimeStepType::System,
     630             :                             OutputProcessor::SOVStoreType::Average,
     631         171 :                             name);
     632         342 :         SetupOutputVariable(state,
     633             :                             "Fan Electricity Energy",
     634             :                             OutputProcessor::Unit::J,
     635             :                             m_fanEnergy,
     636             :                             OutputProcessor::SOVTimeStepType::System,
     637             :                             OutputProcessor::SOVStoreType::Summed,
     638             :                             name,
     639             :                             _,
     640             :                             "Electricity",
     641             :                             "Fans",
     642             :                             m_endUseSubcategoryName,
     643         171 :                             "System");
     644         342 :         SetupOutputVariable(state,
     645             :                             "Fan Air Mass Flow Rate",
     646             :                             OutputProcessor::Unit::kg_s,
     647             :                             m_outletAirMassFlowRate,
     648             :                             OutputProcessor::SOVTimeStepType::System,
     649             :                             OutputProcessor::SOVStoreType::Average,
     650         171 :                             name);
     651         171 :         if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds == 1) {
     652         260 :             SetupOutputVariable(state,
     653             :                                 "Fan Runtime Fraction",
     654             :                                 OutputProcessor::Unit::None,
     655         130 :                                 m_fanRunTimeFractionAtSpeed[0],
     656             :                                 OutputProcessor::SOVTimeStepType::System,
     657             :                                 OutputProcessor::SOVStoreType::Average,
     658         130 :                                 name);
     659          41 :         } else if (speedControl == SpeedControlMethod::Discrete && m_numSpeeds > 1) {
     660          63 :             for (auto speedLoop = 0; speedLoop < m_numSpeeds; ++speedLoop) {
     661         129 :                 SetupOutputVariable(state,
     662          86 :                                     "Fan Runtime Fraction Speed " + fmt::to_string(speedLoop + 1),
     663             :                                     OutputProcessor::Unit::None,
     664          43 :                                     m_fanRunTimeFractionAtSpeed[speedLoop],
     665             :                                     OutputProcessor::SOVTimeStepType::System,
     666             :                                     OutputProcessor::SOVStoreType::Average,
     667             :                                     name);
     668             :             }
     669             :         }
     670             : 
     671         171 :         if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     672          33 :             SetupEMSInternalVariable(state, "Fan Maximum Mass Flow Rate", name, "[kg/s]", m_maxAirMassFlowRate);
     673          33 :             SetupEMSActuator(state, "Fan", name, "Fan Air Mass Flow Rate", "[kg/s]", m_eMSMaxMassFlowOverrideOn, m_eMSAirMassFlowValue);
     674          33 :             SetupEMSInternalVariable(state, "Fan Nominal Pressure Rise", name, "[Pa]", deltaPress);
     675          33 :             SetupEMSActuator(state, "Fan", name, "Fan Pressure Rise", "[Pa]", m_eMSFanPressureOverrideOn, m_eMSFanPressureValue);
     676          33 :             SetupEMSInternalVariable(state, "Fan Nominal Total Efficiency", name, "[fraction]", m_fanTotalEff);
     677          33 :             SetupEMSActuator(state, "Fan", name, "Fan Total Efficiency", "[fraction]", m_eMSFanEffOverrideOn, m_eMSFanEffValue);
     678          99 :             SetupEMSActuator(
     679          66 :                 state, "Fan", name, "Fan Autosized Air Flow Rate", "[m3/s]", m_maxAirFlowRateEMSOverrideOn, m_maxAirFlowRateEMSOverrideValue);
     680             :         }
     681             : 
     682         171 :         if (m_heatLossesDestination == ThermalLossDestination::ZoneGains) {
     683           0 :             SetupZoneInternalGain(state, m_zoneNum, name, DataHeatBalance::IntGainType::FanSystemModel, &m_qdotConvZone, nullptr, &m_qdotRadZone);
     684             :         }
     685             : 
     686         171 :         alphaArgs.deallocate();
     687         171 :         alphaFieldNames.deallocate();
     688         171 :         isAlphaFieldBlank.deallocate();
     689         171 :         numericArgs.deallocate();
     690         171 :         numericFieldNames.deallocate();
     691         171 :         isNumericFieldBlank.deallocate();
     692             : 
     693         171 :         bool anyEMSRan = false;
     694         171 :         EMSManager::ManageEMS(state, EMSManager::EMSCallFrom::ComponentGetInput, anyEMSRan, ObjexxFCL::Optional_int_const());
     695         171 :     }
     696             : 
     697             :     void
     698     7213218 :     FanSystem::calcSimpleSystemFan(EnergyPlusData &state,
     699             :                                    Optional<Real64 const> flowFraction, // Flow fraction for entire timestep (not used if flow ratios are present)
     700             :                                    Optional<Real64 const> pressureRise, // Pressure difference to use for DeltaPress
     701             :                                    Optional<Real64 const> flowRatio1,   // Flow ratio in operating mode 1
     702             :                                    Optional<Real64 const> runTimeFrac1, // Run time fraction in operating mode 1
     703             :                                    Optional<Real64 const> flowRatio2,   // Flow ratio in operating mode 2
     704             :                                    Optional<Real64 const> runTimeFrac2, // Run time fraction in operating mode 2
     705             :                                    Optional<Real64 const> pressureRise2 // Pressure difference to use for operating mode 2
     706             :     )
     707             :     {
     708    14426436 :         std::vector<Real64> localPressureRise; // [0] is operating mode 1, [1] is operating mode 2
     709             :         Real64 localFlowFraction;
     710             :         Real64 localFanTotEff;
     711    14426436 :         std::vector<Real64> localAirMassFlow;
     712    14426436 :         std::vector<Real64> localFlowRatio;
     713    14426436 :         std::vector<Real64> localRunTimeFrac;
     714     7213218 :         bool localUseFlowRatiosAndRunTimeFracs = false;
     715             : 
     716     7213218 :         int localNumModes = 1; // Number of operating modes, 1 or 2 ( e.g. heating, ventilating, cooling)
     717     7213218 :         if (present(flowRatio2) && present(runTimeFrac2)) localNumModes = 2;
     718     7213218 :         localPressureRise.resize(2, 0.0);
     719     7213218 :         localAirMassFlow.resize(2, 0.0);
     720     7213218 :         localFlowRatio.resize(2, 0.0);
     721     7213218 :         localRunTimeFrac.resize(2, 1.0);
     722             : 
     723     7213218 :         if (state.dataHVACGlobal->NightVentOn) {
     724             :             // assume if non-zero inputs for night data then this fan is to be used with that data
     725           0 :             if (m_nightVentPressureDelta > 0.0) {
     726           0 :                 localPressureRise[0] = m_nightVentPressureDelta;
     727           0 :                 localPressureRise[1] = m_nightVentPressureDelta;
     728             :             }
     729             : 
     730           0 :             if (m_maxAirMassFlowRate > 0.0) { // protect div by 0
     731           0 :                 localFlowFraction = m_inletAirMassFlowRate / m_maxAirMassFlowRate;
     732             :             } else {
     733           0 :                 localFlowFraction = 1.0;
     734             :             }
     735           0 :             localAirMassFlow[0] = m_inletAirMassFlowRate;
     736             : 
     737             :         } else { // not in night mode
     738     7213218 :             if (present(pressureRise)) {
     739        4628 :                 localPressureRise[0] = pressureRise;
     740             :             } else {
     741     7208590 :                 localPressureRise[0] = deltaPress;
     742             :             }
     743     7213218 :             if (present(pressureRise2)) {
     744           0 :                 localPressureRise[1] = pressureRise2;
     745             :             } else {
     746     7213218 :                 localPressureRise[1] = deltaPress;
     747             :             }
     748     7213218 :             if (present(flowFraction)) {
     749      936396 :                 localFlowFraction = flowFraction;
     750      936396 :                 localAirMassFlow[0] = localFlowFraction * m_maxAirMassFlowRate;
     751             :             } else {
     752     6276822 :                 if (m_maxAirMassFlowRate > 0.0) { // protect div by 0
     753     6276822 :                     localFlowFraction = m_inletAirMassFlowRate / m_maxAirMassFlowRate;
     754             :                 } else {
     755           0 :                     localFlowFraction = 1.0;
     756             :                 }
     757     6276822 :                 localAirMassFlow[0] = m_inletAirMassFlowRate;
     758             :             }
     759     7213218 :             if (present(flowRatio1) && present(flowRatio2) && present(runTimeFrac1) && present(runTimeFrac2)) {
     760     3008078 :                 localUseFlowRatiosAndRunTimeFracs = true;
     761     3008078 :                 localRunTimeFrac[0] = runTimeFrac1;
     762     3008078 :                 localRunTimeFrac[1] = runTimeFrac2;
     763     3008078 :                 localFlowRatio[0] = flowRatio1;
     764     3008078 :                 localAirMassFlow[0] = localFlowRatio[0] * m_maxAirMassFlowRate * localRunTimeFrac[0];
     765     3008078 :                 localFlowRatio[1] = flowRatio2;
     766     3008078 :                 localAirMassFlow[1] = localFlowRatio[1] * m_maxAirMassFlowRate * localRunTimeFrac[1];
     767             :             } else {
     768     4205140 :                 localRunTimeFrac[0] = 1.0; // if runTimeFracs are not present, assume single-mode operation
     769     4205140 :                 localRunTimeFrac[1] = 0.0; // if runTimeFracs are not present, assume single-mode operation
     770             :             }
     771             :         }
     772             : 
     773     7213218 :         Real64 localFaultMaxAirMassFlow = 0.0;
     774     7213218 :         bool faultActive = false;
     775     7213218 :         Real64 localFaultPressureRise = 0.0;
     776    14426436 :         if (m_faultyFilterFlag && (state.dataFaultsMgr->NumFaultyAirFilter > 0) && (!state.dataGlobal->WarmupFlag) &&
     777     7213218 :             (!state.dataGlobal->DoingSizing) && state.dataGlobal->DoWeathSim && (!m_eMSMaxMassFlowOverrideOn) && (!m_eMSFanPressureOverrideOn)) {
     778           0 :             if (ScheduleManager::GetCurrentScheduleValue(state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).AvaiSchedPtr) > 0) {
     779           0 :                 faultActive = true;
     780           0 :                 Real64 FanDesignFlowRateDec = 0; // Decrease of the Fan Design Volume Flow Rate [m3/sec]
     781           0 :                 FanDesignFlowRateDec = Fans::CalFaultyFanAirFlowReduction(
     782             :                     state,
     783             :                     name,
     784             :                     designAirVolFlowRate,
     785             :                     deltaPress,
     786           0 :                     (ScheduleManager::GetCurrentScheduleValue(
     787           0 :                          state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterPressFracSchePtr) -
     788             :                      1) *
     789           0 :                         deltaPress,
     790           0 :                     state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterFanCurvePtr);
     791             : 
     792           0 :                 localFaultMaxAirMassFlow = m_maxAirMassFlowRate - FanDesignFlowRateDec * m_rhoAirStdInit;
     793             : 
     794           0 :                 localFaultPressureRise =
     795           0 :                     ScheduleManager::GetCurrentScheduleValue(
     796           0 :                         state, state.dataFaultsMgr->FaultsFouledAirFilters(m_faultyFilterIndex).FaultyAirFilterPressFracSchePtr) *
     797           0 :                     deltaPress;
     798             :             }
     799             :         }
     800             : 
     801    17434514 :         for (int mode = 0; mode < localNumModes; ++mode) {
     802             :             // EMS override MassFlow, DeltaPress, and FanEff
     803    10221296 :             if (m_eMSFanPressureOverrideOn) localPressureRise[mode] = m_eMSFanPressureValue;
     804    10221296 :             if (m_eMSFanEffOverrideOn) localFanTotEff = m_eMSFanEffValue;
     805    10221296 :             if (m_eMSMaxMassFlowOverrideOn) {
     806           0 :                 localAirMassFlow[mode] = m_eMSAirMassFlowValue;
     807             :             }
     808             : 
     809    10221296 :             localAirMassFlow[mode] = min(localAirMassFlow[mode], m_maxAirMassFlowRate);
     810    10221296 :             if (faultActive) {
     811           0 :                 localAirMassFlow[mode] = min(localAirMassFlow[mode], localFaultMaxAirMassFlow);
     812           0 :                 localPressureRise[mode] = localFaultPressureRise;
     813             :             }
     814    10221296 :             localFlowFraction = localAirMassFlow[0] / m_maxAirMassFlowRate;
     815    10221296 :             localFlowFraction = min(1.0, localFlowFraction);
     816             : 
     817    10221296 :             if (localRunTimeFrac[mode] > 0.0) {
     818     7629948 :                 localFlowRatio[mode] = localAirMassFlow[mode] / (m_maxAirMassFlowRate * localRunTimeFrac[mode]);
     819             :             }
     820    10221296 :             localFlowRatio[mode] = min(1.0, localFlowRatio[mode]);
     821             :         }
     822             : 
     823             :         // zero these now, because the may accumulate across multiple operating modes
     824     7213218 :         m_powerLossToAir = 0.0;
     825     7213218 :         m_fanPower = 0.0;
     826     7213218 :         m_outletAirMassFlowRate = 0.0;
     827     7213218 :         if (speedControl == SpeedControlMethod::Discrete) {
     828    14910145 :             for (auto loop = 0; loop < m_numSpeeds; ++loop) {
     829     8307133 :                 m_fanRunTimeFractionAtSpeed[loop] = 0.0;
     830             :             }
     831             :         }
     832             : 
     833    14183572 :         if ((ScheduleManager::GetCurrentScheduleValue(state, availSchedIndex) > 0.0 || m_objTurnFansOn) && !m_objTurnFansOff &&
     834     6970354 :             ((localAirMassFlow[0] + localAirMassFlow[1]) > 0.0)) {
     835             :             // fan is running
     836             : 
     837    14996976 :             for (int mode = 0; mode < localNumModes; ++mode) {
     838             : 
     839             :                 // if no flow for this mode then continue to the next mode
     840     8844859 :                 if (localAirMassFlow[mode] == 0.0) continue;
     841             : 
     842     6846631 :                 switch (speedControl) {
     843             : 
     844     6330470 :                 case SpeedControlMethod::Discrete: {
     845             :                     //
     846     6330470 :                     if (state.dataHVACGlobal->OnOffFanPartLoadFraction <= 0.0) {
     847           0 :                         state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0;
     848             :                     }
     849     6330470 :                     if (state.dataHVACGlobal->OnOffFanPartLoadFraction < 0.7) {
     850        2124 :                         state.dataHVACGlobal->OnOffFanPartLoadFraction =
     851             :                             0.7; // a warning message is already issued from the DX coils or gas heating coil
     852             :                     }
     853     6330470 :                     if (localUseFlowRatiosAndRunTimeFracs) {
     854             :                         // Use flow ratios and runtimefractions pass from parent (allows fan to cycle at a specified speed)
     855     3231922 :                         Real64 locRunTimeFraction(0.0);
     856     3231922 :                         if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
     857     3059507 :                             locRunTimeFraction = localRunTimeFrac[mode];
     858             :                         } else {
     859      172415 :                             locRunTimeFraction = max(0.0, min(1.0, localRunTimeFrac[mode] / state.dataHVACGlobal->OnOffFanPartLoadFraction));
     860             :                         }
     861     3231922 :                         Real64 locFlowRatio = localFlowRatio[mode]; // Current mode flow rate / max flow rate
     862     3231922 :                         Real64 locLowSpeedFanRunTimeFrac = 0.0;
     863     3231922 :                         Real64 locHiSpeedFanRunTimeFrac = 0.0;
     864     3231922 :                         if (m_numSpeeds == 1) { // CV or OnOff
     865     1717572 :                             localFanTotEff = m_fanTotalEff;
     866     1717572 :                             locHiSpeedFanRunTimeFrac = locRunTimeFraction * locFlowRatio;
     867     1717572 :                             m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
     868     1717572 :                             m_fanPower += max(
     869     1717572 :                                 0.0, locHiSpeedFanRunTimeFrac * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit));
     870     1514350 :                         } else if (m_numSpeeds > 1) { // multi speed
     871             : 
     872             :                             // find which two speed levels bracket flow ratios and calculate runtimefraction at each speed
     873             :                             // ideally the flow ratios passed in will match one of the fan m_flowFractionAtSpeed but it is not required
     874     1514350 :                             int lowSideSpeed = -1;
     875     1514350 :                             int hiSideSpeed = -1;
     876             : 
     877     1514350 :                             if (locFlowRatio <= m_flowFractionAtSpeed[0]) { // on/off at lowest speed
     878      640964 :                                 hiSideSpeed = 0;
     879      640964 :                                 locHiSpeedFanRunTimeFrac = locFlowRatio * locRunTimeFraction / m_flowFractionAtSpeed[0];
     880      640964 :                                 m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
     881             :                             } else {
     882      873386 :                                 lowSideSpeed = 0; // hush up cppcheck
     883      873386 :                                 hiSideSpeed = 0;  // hush up cppcheck
     884      873386 :                                 for (auto loop = 0; loop < m_numSpeeds - 1; ++loop) {
     885      873386 :                                     if ((m_flowFractionAtSpeed[loop] <= locFlowRatio) && (locFlowRatio <= m_flowFractionAtSpeed[loop + 1])) {
     886      873386 :                                         lowSideSpeed = loop;
     887      873386 :                                         hiSideSpeed = loop + 1;
     888      873386 :                                         break;
     889             :                                     }
     890             :                                 }
     891      873386 :                                 Real64 locLowSpeedTimeFrac = (m_flowFractionAtSpeed[hiSideSpeed] - locFlowRatio) /
     892      873386 :                                                              (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
     893      873386 :                                 locLowSpeedFanRunTimeFrac = locLowSpeedTimeFrac * localRunTimeFrac[mode];
     894      873386 :                                 locHiSpeedFanRunTimeFrac = (1 - locLowSpeedTimeFrac) * localRunTimeFrac[mode];
     895      873386 :                                 m_fanRunTimeFractionAtSpeed[lowSideSpeed] += locLowSpeedFanRunTimeFrac;
     896      873386 :                                 m_fanRunTimeFractionAtSpeed[hiSideSpeed] += locHiSpeedFanRunTimeFrac;
     897             :                             }
     898     1514350 :                             if (lowSideSpeed != -1 && hiSideSpeed != -1) {
     899      873386 :                                 m_fanPower += max(0.0,
     900     1746772 :                                                   locLowSpeedFanRunTimeFrac * m_massFlowAtSpeed[lowSideSpeed] * localPressureRise[mode] /
     901      873386 :                                                           (m_totEfficAtSpeed[lowSideSpeed] * m_rhoAirStdInit) +
     902     1746772 :                                                       locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
     903      873386 :                                                           (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
     904      640964 :                             } else if (lowSideSpeed == -1 && hiSideSpeed == 0) {
     905      640964 :                                 m_fanPower += max(0.0,
     906      640964 :                                                   locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
     907      640964 :                                                       (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
     908             :                             }
     909             :                         }
     910             :                     } else {
     911             :                         // Use localFlowFraction which is not locked at a particular flow ratio (legacy method for fan:onoff)
     912     3098548 :                         Real64 locFanRunTimeFraction(0.0);
     913     3098548 :                         Real64 locLowSpeedFanRunTimeFrac = 0.0;
     914     3098548 :                         Real64 locHiSpeedFanRunTimeFrac = 0.0;
     915     3098548 :                         if (state.dataHVACGlobal->OnOffFanPartLoadFraction >= 1.0) {
     916     3085641 :                             locFanRunTimeFraction = localFlowFraction;
     917             :                         } else {
     918       12907 :                             locFanRunTimeFraction = max(0.0, min(1.0, localFlowFraction / state.dataHVACGlobal->OnOffFanPartLoadFraction));
     919             :                         }
     920     3098548 :                         if (m_numSpeeds == 1) { // CV or OnOff
     921     2906783 :                             localFanTotEff = m_fanTotalEff;
     922     2906783 :                             locHiSpeedFanRunTimeFrac = locFanRunTimeFraction;
     923     2906783 :                             m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
     924     2906783 :                             m_fanPower += max(
     925     2906783 :                                 0.0, locHiSpeedFanRunTimeFrac * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit));
     926      191765 :                         } else if (m_numSpeeds > 1) { // multi speed
     927             : 
     928             :                             // find which two speed levels bracket flow fraction and calculate runtimefraction
     929      191765 :                             int lowSideSpeed = -1;
     930      191765 :                             int hiSideSpeed = -1;
     931             : 
     932      191765 :                             if (locFanRunTimeFraction < m_flowFractionAtSpeed[0]) { // on/off between zero and lowest speed
     933        5090 :                                 hiSideSpeed = 0;
     934        5090 :                                 locHiSpeedFanRunTimeFrac = locFanRunTimeFraction / m_flowFractionAtSpeed[0];
     935        5090 :                                 m_fanRunTimeFractionAtSpeed[0] += locHiSpeedFanRunTimeFrac;
     936             :                             } else {
     937      186675 :                                 lowSideSpeed = 0; // hush up cppcheck
     938      186675 :                                 hiSideSpeed = 0;  // hush up cppcheck
     939      192965 :                                 for (auto loop = 0; loop < m_numSpeeds - 1; ++loop) {
     940      385930 :                                     if ((m_flowFractionAtSpeed[loop] <= locFanRunTimeFraction) &&
     941      192965 :                                         (locFanRunTimeFraction <= m_flowFractionAtSpeed[loop + 1])) {
     942      186675 :                                         lowSideSpeed = loop;
     943      186675 :                                         hiSideSpeed = loop + 1;
     944      186675 :                                         break;
     945             :                                     }
     946             :                                 }
     947      373350 :                                 locLowSpeedFanRunTimeFrac = (m_flowFractionAtSpeed[hiSideSpeed] - locFanRunTimeFraction) /
     948      186675 :                                                             (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
     949      373350 :                                 locHiSpeedFanRunTimeFrac = (locFanRunTimeFraction - m_flowFractionAtSpeed[lowSideSpeed]) /
     950      186675 :                                                            (m_flowFractionAtSpeed[hiSideSpeed] - m_flowFractionAtSpeed[lowSideSpeed]);
     951      186675 :                                 m_fanRunTimeFractionAtSpeed[lowSideSpeed] += locLowSpeedFanRunTimeFrac;
     952      186675 :                                 m_fanRunTimeFractionAtSpeed[hiSideSpeed] += locHiSpeedFanRunTimeFrac;
     953             :                             }
     954      191765 :                             if (lowSideSpeed != -1 && hiSideSpeed != -1) {
     955      186675 :                                 m_fanPower += max(0.0,
     956      373350 :                                                   locLowSpeedFanRunTimeFrac * m_massFlowAtSpeed[lowSideSpeed] * localPressureRise[mode] /
     957      186675 :                                                           (m_totEfficAtSpeed[lowSideSpeed] * m_rhoAirStdInit) +
     958      373350 :                                                       locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
     959      186675 :                                                           (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
     960        5090 :                             } else if (lowSideSpeed == -1 && hiSideSpeed == 0) {
     961        5090 :                                 m_fanPower += max(0.0,
     962        5090 :                                                   locHiSpeedFanRunTimeFrac * m_massFlowAtSpeed[hiSideSpeed] * localPressureRise[mode] /
     963        5090 :                                                       (m_totEfficAtSpeed[hiSideSpeed] * m_rhoAirStdInit));
     964             :                             }
     965             :                         }
     966             :                     }
     967     6330470 :                     localFanTotEff = m_fanTotalEff;
     968     6330470 :                     break;
     969             :                 }
     970      516161 :                 case SpeedControlMethod::Continuous: {
     971      516161 :                     localFanTotEff = m_fanTotalEff;
     972      516161 :                     Real64 locFlowRatio(0.0);
     973      516161 :                     Real64 locFanRunTimeFraction(0.0);
     974      516161 :                     if (localUseFlowRatiosAndRunTimeFracs) {
     975      155334 :                         locFlowRatio = localFlowRatio[mode];
     976      155334 :                         locFanRunTimeFraction = localRunTimeFrac[mode];
     977             :                     } else {
     978      360827 :                         locFlowRatio = localFlowFraction;
     979      360827 :                         locFanRunTimeFraction = 1.0;
     980             :                     }
     981             : 
     982      516161 :                     Real64 localFlowFractionForPower = max(m_minPowerFlowFrac, locFlowRatio);
     983      516161 :                     Real64 localPowerFraction(0.0);
     984      516161 :                     if (state.dataHVACGlobal->NightVentOn) {
     985           0 :                         localPowerFraction = 1.0; // not sure why, but legacy fan had this for night ventilation
     986             :                     } else {
     987      516161 :                         localPowerFraction = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, localFlowFractionForPower);
     988             :                     }
     989      516161 :                     Real64 localfanPower = max(0.0,
     990      516161 :                                                locFanRunTimeFraction * localPowerFraction * m_maxAirMassFlowRate * localPressureRise[mode] /
     991     1032322 :                                                    (localFanTotEff * m_rhoAirStdInit));
     992      516161 :                     Real64 fanShaftPower = m_motorEff * localfanPower;
     993      516161 :                     Real64 localpowerLossToAir = fanShaftPower + (localfanPower - fanShaftPower) * m_motorInAirFrac;
     994      516161 :                     m_outletAirEnthalpy = m_inletAirEnthalpy + localpowerLossToAir / localAirMassFlow[mode]; // this will get revised later
     995      516161 :                     m_outletAirHumRat = m_inletAirHumRat;                                                    // this will get revised later
     996      516161 :                     m_outletAirTemp = Psychrometrics::PsyTdbFnHW(m_outletAirEnthalpy, m_outletAirHumRat);    // this will get revised later
     997             :                     // When fan air flow is less than 10%, the fan power curve is linearized between the 10% to 0% to
     998             :                     //  avoid the unrealistic high temperature rise across the fan.
     999      516161 :                     Real64 deltaTAcrossFan = m_outletAirTemp - m_inletAirTemp;
    1000      516161 :                     if (deltaTAcrossFan > 20.0) {
    1001           0 :                         Real64 minFlowFracLimitFanHeat = 0.10;
    1002           0 :                         Real64 powerFractionAtLowMin = 0.0;
    1003           0 :                         Real64 fanPoweratLowMinimum = 0.0;
    1004           0 :                         if (localFlowFractionForPower < minFlowFracLimitFanHeat) {
    1005           0 :                             powerFractionAtLowMin = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, minFlowFracLimitFanHeat);
    1006           0 :                             fanPoweratLowMinimum =
    1007           0 :                                 powerFractionAtLowMin * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit);
    1008           0 :                             localfanPower = max(0.0, localFlowFractionForPower * fanPoweratLowMinimum / minFlowFracLimitFanHeat);
    1009           0 :                         } else if (locFlowRatio < minFlowFracLimitFanHeat) {
    1010           0 :                             powerFractionAtLowMin = Curve::CurveValue(state, powerModFuncFlowFractionCurveIndex, minFlowFracLimitFanHeat);
    1011           0 :                             fanPoweratLowMinimum =
    1012           0 :                                 powerFractionAtLowMin * m_maxAirMassFlowRate * localPressureRise[mode] / (localFanTotEff * m_rhoAirStdInit);
    1013           0 :                             localfanPower = max(0.0, locFlowRatio * fanPoweratLowMinimum / minFlowFracLimitFanHeat);
    1014             :                         }
    1015             :                     }
    1016      516161 :                     m_fanPower += localfanPower;
    1017      516161 :                     break;
    1018             :                 } // continuous speed control case
    1019           0 :                 case SpeedControlMethod::NotSet: {
    1020             :                     // do nothing
    1021           0 :                     break;
    1022             :                 }
    1023           0 :                 default:
    1024           0 :                     assert(false);
    1025             :                 } // end switch
    1026     6846631 :                 m_outletAirMassFlowRate += localAirMassFlow[mode];
    1027             : 
    1028             :             } // end of operating mode loop
    1029             : 
    1030     6152117 :             if (m_outletAirMassFlowRate > 0.0) {
    1031     6152117 :                 Real64 fanShaftPower = m_motorEff * m_fanPower; // power delivered to shaft
    1032     6152117 :                 m_powerLossToAir = fanShaftPower + (m_fanPower - fanShaftPower) * m_motorInAirFrac;
    1033     6152117 :                 m_outletAirEnthalpy = m_inletAirEnthalpy + m_powerLossToAir / m_outletAirMassFlowRate;
    1034             :                 // This fan does not change the moisture or Mass Flow across the component
    1035     6152117 :                 m_outletAirHumRat = m_inletAirHumRat;
    1036     6152117 :                 m_outletAirTemp = Psychrometrics::PsyTdbFnHW(m_outletAirEnthalpy, m_outletAirHumRat);
    1037             :             } else {
    1038           0 :                 m_fanPower = 0.0;
    1039           0 :                 m_powerLossToAir = 0.0;
    1040           0 :                 m_outletAirHumRat = m_inletAirHumRat;
    1041           0 :                 m_outletAirEnthalpy = m_inletAirEnthalpy;
    1042           0 :                 m_outletAirTemp = m_inletAirTemp;
    1043           0 :                 m_massFlowRateMaxAvail = 0.0;
    1044           0 :                 m_massFlowRateMinAvail = 0.0;
    1045             :             }
    1046             : 
    1047             :         } else { // fan is off
    1048             :             // Fan is off and not operating no power consumed and mass flow rate.
    1049     1061101 :             m_fanPower = 0.0;
    1050     1061101 :             m_powerLossToAir = 0.0;
    1051     1061101 :             m_outletAirHumRat = m_inletAirHumRat;
    1052     1061101 :             m_outletAirEnthalpy = m_inletAirEnthalpy;
    1053     1061101 :             m_outletAirTemp = m_inletAirTemp;
    1054             :             // Set the Control Flow variables to 0.0 flow when OFF.
    1055     1061101 :             if (fanIsSecondaryDriver) {
    1056       72239 :                 m_outletAirMassFlowRate =
    1057      144478 :                     localAirMassFlow[0] +
    1058       72239 :                     localAirMassFlow[1]; // sometimes the air is moving with the fan off, eg. AirTerminal:SingleDuct:VAV:Reheat:VariableSpeedFan
    1059       72239 :                 if (m_outletAirMassFlowRate == 0.0) {
    1060          52 :                     m_massFlowRateMaxAvail = 0.0;
    1061          52 :                     m_massFlowRateMinAvail = 0.0;
    1062             :                 }
    1063             :             } else {
    1064      988862 :                 m_outletAirMassFlowRate = 0.0;
    1065      988862 :                 m_massFlowRateMaxAvail = 0.0;
    1066      988862 :                 m_massFlowRateMinAvail = 0.0;
    1067             :             }
    1068             :         }
    1069             : 
    1070     7213218 :         if (m_heatLossesDestination == ThermalLossDestination::ZoneGains) {
    1071           0 :             Real64 powerLossToZone = m_fanPower - m_powerLossToAir;
    1072           0 :             m_qdotConvZone = powerLossToZone * (1.0 - m_zoneRadFract);
    1073           0 :             m_qdotRadZone = powerLossToZone * m_zoneRadFract;
    1074             :         }
    1075     7213218 :         state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // reset to 1
    1076     7213218 :     }
    1077             : 
    1078     7213218 :     void FanSystem::update(EnergyPlusData &state) const // does not change state of object, only update elsewhere
    1079             :     {
    1080             :         // Set the outlet air node of the fan
    1081     7213218 :         state.dataLoopNodes->Node(outletNodeNum).MassFlowRate = m_outletAirMassFlowRate;
    1082     7213218 :         state.dataLoopNodes->Node(outletNodeNum).Temp = m_outletAirTemp;
    1083     7213218 :         state.dataLoopNodes->Node(outletNodeNum).HumRat = m_outletAirHumRat;
    1084     7213218 :         state.dataLoopNodes->Node(outletNodeNum).Enthalpy = m_outletAirEnthalpy;
    1085             :         // Set the outlet nodes for properties that just pass through & not used
    1086     7213218 :         state.dataLoopNodes->Node(outletNodeNum).Quality = state.dataLoopNodes->Node(inletNodeNum).Quality;
    1087     7213218 :         state.dataLoopNodes->Node(outletNodeNum).Press = state.dataLoopNodes->Node(inletNodeNum).Press;
    1088             : 
    1089             :         // Set the Node Flow Control Variables from the Fan Control Variables
    1090     7213218 :         state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMaxAvail = m_massFlowRateMaxAvail;
    1091     7213218 :         state.dataLoopNodes->Node(outletNodeNum).MassFlowRateMinAvail = m_massFlowRateMinAvail;
    1092             : 
    1093             :         // make sure inlet has the same mass flow
    1094     7213218 :         state.dataLoopNodes->Node(inletNodeNum).MassFlowRate = m_outletAirMassFlowRate;
    1095             : 
    1096     7213218 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
    1097           0 :             state.dataLoopNodes->Node(outletNodeNum).CO2 = state.dataLoopNodes->Node(inletNodeNum).CO2;
    1098             :         }
    1099     7213218 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
    1100           0 :             state.dataLoopNodes->Node(outletNodeNum).GenContam = state.dataLoopNodes->Node(inletNodeNum).GenContam;
    1101             :         }
    1102             : 
    1103     7213218 :         if (speedControl == SpeedControlMethod::Continuous) {
    1104      610206 :             if (AirLoopNum > 0) {
    1105           0 :                 state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_fanRunTimeFractionAtSpeed[0];
    1106             :             }
    1107             :         } else {
    1108     6603012 :             if (AirLoopNum > 0) {
    1109      108550 :                 if (m_numSpeeds == 1) {
    1110      108550 :                     state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_outletAirMassFlowRate / m_maxAirMassFlowRate;
    1111             :                 } else {
    1112           0 :                     if (m_outletAirMassFlowRate <= m_massFlowAtSpeed[0]) {
    1113           0 :                         state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = m_outletAirMassFlowRate / m_massFlowAtSpeed[0];
    1114             :                     } else {
    1115           0 :                         state.dataAirLoop->AirLoopAFNInfo(AirLoopNum).AFNLoopOnOffFanRTF = 1.0;
    1116             :                     }
    1117             :                 }
    1118             :             }
    1119             :         }
    1120     7213218 :     }
    1121             : 
    1122     7213218 :     void FanSystem::report(EnergyPlusData &state)
    1123             :     {
    1124     7213218 :         m_fanEnergy = m_fanPower * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
    1125     7213218 :         m_deltaTemp = m_outletAirTemp - m_inletAirTemp;
    1126     7213218 :     }
    1127             : 
    1128     2452399 :     Real64 FanSystem::fanPower() const
    1129             :     {
    1130     2452399 :         return m_fanPower;
    1131             :     }
    1132             : 
    1133           0 :     Real64 FanSystem::powerLossToAir() const
    1134             :     {
    1135           0 :         return m_powerLossToAir;
    1136             :     }
    1137             : 
    1138         225 :     Real64 FanSystem::maxAirMassFlowRate() const
    1139             :     {
    1140         225 :         return m_maxAirMassFlowRate;
    1141             :     }
    1142             : 
    1143           0 :     Real64 FanSystem::getFanDesignTemperatureRise(EnergyPlusData &state) const
    1144             :     {
    1145           0 :         if (!m_objSizingFlag) {
    1146           0 :             Real64 cpAir = Psychrometrics::PsyCpAirFnW(DataPrecisionGlobals::constant_zero);
    1147           0 :             Real64 designDeltaT = (deltaPress / (m_rhoAirStdInit * cpAir * m_fanTotalEff)) * (m_motorEff + m_motorInAirFrac * (1.0 - m_motorEff));
    1148           0 :             return designDeltaT;
    1149             :         } else {
    1150             :             // TODO throw warning, exception, call sizing?
    1151           0 :             ShowWarningError(state, "FanSystem::getFanDesignTemperatureRise called before fan sizing completed ");
    1152           0 :             return 0.0;
    1153             :         }
    1154             :     }
    1155             : 
    1156          12 :     Real64 FanSystem::getFanDesignHeatGain(EnergyPlusData &state, Real64 const FanVolFlow // fan volume flow rate [m3/s]
    1157             :     )
    1158             :     {
    1159          12 :         if (!m_objSizingFlag) {
    1160          12 :             Real64 fanPowerTot = (FanVolFlow * deltaPress) / m_fanTotalEff;
    1161          12 :             Real64 designHeatGain = m_motorEff * fanPowerTot + (fanPowerTot - m_motorEff * fanPowerTot) * m_motorInAirFrac;
    1162          12 :             return designHeatGain;
    1163             :         } else {
    1164           0 :             set_size(state);
    1165           0 :             Real64 fanPowerTot = (FanVolFlow * deltaPress) / m_fanTotalEff;
    1166           0 :             Real64 designHeatGain = m_motorEff * fanPowerTot + (fanPowerTot - m_motorEff * fanPowerTot) * m_motorInAirFrac;
    1167           0 :             return designHeatGain;
    1168             :         }
    1169             :     }
    1170             : 
    1171        1564 :     void FanSystem::FanInputsForDesignHeatGain(EnergyPlusData &state, Real64 &deltaP, Real64 &motEff, Real64 &totEff, Real64 &motInAirFrac)
    1172             :     {
    1173        1564 :         if (!m_objSizingFlag) {
    1174        1447 :             deltaP = deltaPress;
    1175        1447 :             motEff = m_motorEff;
    1176        1447 :             totEff = m_fanTotalEff;
    1177        1447 :             motInAirFrac = m_motorInAirFrac;
    1178        1447 :             return;
    1179             :         } else {
    1180         117 :             set_size(state);
    1181         117 :             deltaP = deltaPress;
    1182         117 :             motEff = m_motorEff;
    1183         117 :             totEff = m_fanTotalEff;
    1184         117 :             motInAirFrac = m_motorInAirFrac;
    1185         117 :             return;
    1186             :         }
    1187             :     }
    1188             : 
    1189             :     // void
    1190             :     // FanSystem::fanIsSecondaryDriver()
    1191             :     //{
    1192             :     //    // this concept is used when the fan may be operating in a situation where there is airflow without it running at all
    1193             :     //    // call this when some other fan is feeding the device containing this fan, making it a secondary fan.
    1194             :     //    // example is the fan in a VS VAV air terminal used for UFAD.
    1195             :     //    fanIsSecondaryDriver = true;
    1196             :     //}
    1197             : 
    1198             :     // void
    1199             :     // FanSystem::setFaultyFilterOn()
    1200             :     //{
    1201             :     //    // call this to set flag to direct model to use fault for filter
    1202             :     //    faultyFilterFlag_ = true;
    1203             :     //}
    1204             : 
    1205             :     // void
    1206             :     // FanSystem::setFaultyFilterIndex( int const faultyAirFilterIndex  )
    1207             :     //{
    1208             :     //    // this is the index in the FaultsFouledAirFilters structure array in FaultsManager
    1209             :     //    m_faultyFilterIndex = faultyAirFilterIndex;
    1210             :     //}
    1211             : 
    1212             : } // namespace HVACFan
    1213             : 
    1214        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13