LCOV - code coverage report
Current view: top level - EnergyPlus - UnitHeater.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 53.7 % 905 486
Test Date: 2025-06-02 07:23:51 Functions: 77.8 % 9 7

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Array.functions.hh>
      53              : 
      54              : // EnergyPlus Headers
      55              : #include <EnergyPlus/Autosizing/HeatingAirFlowSizing.hh>
      56              : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
      57              : #include <EnergyPlus/BranchNodeConnections.hh>
      58              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59              : #include <EnergyPlus/DataEnvironment.hh>
      60              : #include <EnergyPlus/DataHVACGlobals.hh>
      61              : #include <EnergyPlus/DataHeatBalance.hh>
      62              : #include <EnergyPlus/DataLoopNode.hh>
      63              : #include <EnergyPlus/DataSizing.hh>
      64              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      65              : #include <EnergyPlus/DataZoneEquipment.hh>
      66              : #include <EnergyPlus/Fans.hh>
      67              : #include <EnergyPlus/FluidProperties.hh>
      68              : #include <EnergyPlus/General.hh>
      69              : #include <EnergyPlus/GeneralRoutines.hh>
      70              : #include <EnergyPlus/HeatingCoils.hh>
      71              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      72              : #include <EnergyPlus/NodeInputManager.hh>
      73              : #include <EnergyPlus/OutputProcessor.hh>
      74              : #include <EnergyPlus/PlantUtilities.hh>
      75              : #include <EnergyPlus/Psychrometrics.hh>
      76              : #include <EnergyPlus/ReportCoilSelection.hh>
      77              : #include <EnergyPlus/ScheduleManager.hh>
      78              : #include <EnergyPlus/SteamCoils.hh>
      79              : #include <EnergyPlus/UnitHeater.hh>
      80              : #include <EnergyPlus/UtilityRoutines.hh>
      81              : #include <EnergyPlus/WaterCoils.hh>
      82              : 
      83              : namespace EnergyPlus {
      84              : 
      85              : namespace UnitHeater {
      86              : 
      87              :     // Module containing the routines dealing with the Unit Heater
      88              : 
      89              :     // MODULE INFORMATION:
      90              :     //       AUTHOR         Rick Strand
      91              :     //       DATE WRITTEN   May 2000
      92              :     //       MODIFIED       Brent Griffith, Sept 2010, plant upgrades, fluid properties
      93              :     //       MODIFIED       Bereket Nigusse, FSEC, October 2013, Added cycling fan operating mode
      94              : 
      95              :     // PURPOSE OF THIS MODULE:
      96              :     // To simulate unit heaters.  It is assumed that unit heaters are zone equipment
      97              :     // without any connection to outside air other than through a separately defined
      98              :     // air loop.
      99              : 
     100              :     // METHODOLOGY EMPLOYED:
     101              :     // Units are modeled as a collection of a fan and a heating coil.  The fan
     102              :     // can either be a continuously running fan or an on-off fan which turns on
     103              :     // only when there is actually a heating load.  This fan control works together
     104              :     // with the unit operation schedule to determine what the unit heater actually
     105              :     // does at a given point in time.
     106              : 
     107              :     // REFERENCES:
     108              :     // ASHRAE Systems and Equipment Handbook (SI), 1996. pp. 31.3-31.8
     109              :     // Rick Strand's unit heater module which was based upon Fred Buhl's fan coil
     110              :     // module (FanCoilUnits.cc)
     111              : 
     112       638585 :     void SimUnitHeater(EnergyPlusData &state,
     113              :                        std::string_view CompName,     // name of the fan coil unit
     114              :                        int const ZoneNum,             // number of zone being served
     115              :                        bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
     116              :                        Real64 &PowerMet,              // Sensible power supplied (W)
     117              :                        Real64 &LatOutputProvided,     // Latent add/removal supplied by window AC (kg/s), dehumid = negative
     118              :                        int &CompIndex)
     119              :     {
     120              : 
     121              :         // SUBROUTINE INFORMATION:
     122              :         //       AUTHOR         Rick Strand
     123              :         //       DATE WRITTEN   May 2000
     124              :         //       MODIFIED       Don Shirey, Aug 2009 (LatOutputProvided)
     125              : 
     126              :         // PURPOSE OF THIS SUBROUTINE:
     127              :         // This is the main driver subroutine for the Unit Heater simulation.
     128              : 
     129              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     130              :         int UnitHeatNum; // index of unit heater being simulated
     131              : 
     132       638585 :         if (state.dataUnitHeaters->GetUnitHeaterInputFlag) {
     133           14 :             GetUnitHeaterInput(state);
     134           14 :             state.dataUnitHeaters->GetUnitHeaterInputFlag = false;
     135              :         }
     136              : 
     137              :         // Find the correct Unit Heater Equipment
     138       638585 :         if (CompIndex == 0) {
     139           63 :             UnitHeatNum = Util::FindItemInList(CompName, state.dataUnitHeaters->UnitHeat);
     140           63 :             if (UnitHeatNum == 0) {
     141            0 :                 ShowFatalError(state, format("SimUnitHeater: Unit not found={}", CompName));
     142              :             }
     143           63 :             CompIndex = UnitHeatNum;
     144              :         } else {
     145       638522 :             UnitHeatNum = CompIndex;
     146       638522 :             if (UnitHeatNum > state.dataUnitHeaters->NumOfUnitHeats || UnitHeatNum < 1) {
     147            0 :                 ShowFatalError(state,
     148            0 :                                format("SimUnitHeater:  Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
     149              :                                       UnitHeatNum,
     150            0 :                                       state.dataUnitHeaters->NumOfUnitHeats,
     151              :                                       CompName));
     152              :             }
     153       638522 :             if (state.dataUnitHeaters->CheckEquipName(UnitHeatNum)) {
     154           63 :                 if (CompName != state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name) {
     155            0 :                     ShowFatalError(state,
     156            0 :                                    format("SimUnitHeater: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
     157              :                                           UnitHeatNum,
     158              :                                           CompName,
     159            0 :                                           state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
     160              :                 }
     161           63 :                 state.dataUnitHeaters->CheckEquipName(UnitHeatNum) = false;
     162              :             }
     163              :         }
     164              : 
     165       638585 :         state.dataSize->ZoneEqUnitHeater = true;
     166              : 
     167       638585 :         InitUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration);
     168              : 
     169       638585 :         state.dataSize->ZoneHeatingOnlyFan = true;
     170              : 
     171       638585 :         CalcUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration, PowerMet, LatOutputProvided);
     172              : 
     173       638585 :         state.dataSize->ZoneHeatingOnlyFan = false;
     174              : 
     175              :         //  CALL UpdateUnitHeater
     176              : 
     177       638585 :         ReportUnitHeater(state, UnitHeatNum);
     178              : 
     179       638585 :         state.dataSize->ZoneEqUnitHeater = false;
     180       638585 :     }
     181              : 
     182           14 :     void GetUnitHeaterInput(EnergyPlusData &state)
     183              :     {
     184              : 
     185              :         // SUBROUTINE INFORMATION:
     186              :         //       AUTHOR         Rick Strand
     187              :         //       DATE WRITTEN   May 2000
     188              :         //       MODIFIED       Chandan Sharma, FSEC, March 2011: Added ZoneHVAC sys avail manager
     189              :         //                      Bereket Nigusse, FSEC, April 2011: eliminated input node names
     190              :         //                                                         & added fan object type
     191              : 
     192              :         // PURPOSE OF THIS SUBROUTINE:
     193              :         // Obtain the user input data for all of the unit heaters in the input file.
     194              : 
     195              :         // METHODOLOGY EMPLOYED:
     196              :         // Standard EnergyPlus methodology.
     197              : 
     198              :         // REFERENCES:
     199              :         // Fred Buhl's fan coil module (FanCoilUnits.cc)
     200              : 
     201              :         static constexpr std::string_view RoutineName("GetUnitHeaterInput: "); // include trailing blank space
     202              :         static constexpr std::string_view routineName = "GetUnitHeaterInput";  // include trailing blank space
     203              : 
     204              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     205           14 :         bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
     206              :         int IOStatus;            // Used in GetObjectItem
     207              :         bool IsNotOK;            // TRUE if there was a problem with a list name
     208           14 :         bool errFlag(false);     // interim error flag
     209              :         int NumAlphas;           // Number of Alphas for each GetObjectItem call
     210              :         int NumNumbers;          // Number of Numbers for each GetObjectItem call
     211              :         int NumFields;           // Total number of fields in object
     212              : 
     213              :         Real64 FanVolFlow;             // Fan volumetric flow rate
     214           14 :         Array1D_string Alphas;         // Alpha items for object
     215           14 :         Array1D<Real64> Numbers;       // Numeric items for object
     216           14 :         Array1D_string cAlphaFields;   // Alpha field names
     217           14 :         Array1D_string cNumericFields; // Numeric field names
     218           14 :         Array1D_bool lAlphaBlanks;     // Logical array, alpha field input BLANK = .TRUE.
     219           14 :         Array1D_bool lNumericBlanks;   // Logical array, numeric field input BLANK = .TRUE.
     220              :         int CtrlZone;                  // index to loop counter
     221              :         int NodeNum;                   // index to loop counter
     222              : 
     223              :         // Figure out how many unit heaters there are in the input file
     224           14 :         std::string CurrentModuleObject = state.dataUnitHeaters->cMO_UnitHeater;
     225           14 :         state.dataUnitHeaters->NumOfUnitHeats = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     226           14 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
     227              : 
     228           14 :         Alphas.allocate(NumAlphas);
     229           14 :         Numbers.dimension(NumNumbers, 0.0);
     230           14 :         cAlphaFields.allocate(NumAlphas);
     231           14 :         cNumericFields.allocate(NumNumbers);
     232           14 :         lAlphaBlanks.dimension(NumAlphas, true);
     233           14 :         lNumericBlanks.dimension(NumNumbers, true);
     234              : 
     235              :         // Allocate the local derived type and do one-time initializations for all parts of it
     236           14 :         if (state.dataUnitHeaters->NumOfUnitHeats > 0) {
     237           14 :             state.dataUnitHeaters->UnitHeat.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     238           14 :             state.dataUnitHeaters->CheckEquipName.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     239           14 :             state.dataUnitHeaters->UnitHeatNumericFields.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     240              :         }
     241           14 :         state.dataUnitHeaters->CheckEquipName = true;
     242              : 
     243           77 :         for (int UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats;
     244              :              ++UnitHeatNum) { // Begin looping over all of the unit heaters found in the input file...
     245              : 
     246           63 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     247              :                                                                      CurrentModuleObject,
     248              :                                                                      UnitHeatNum,
     249              :                                                                      Alphas,
     250              :                                                                      NumAlphas,
     251              :                                                                      Numbers,
     252              :                                                                      NumNumbers,
     253              :                                                                      IOStatus,
     254              :                                                                      lNumericBlanks,
     255              :                                                                      lAlphaBlanks,
     256              :                                                                      cAlphaFields,
     257              :                                                                      cNumericFields);
     258              : 
     259           63 :             ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
     260              : 
     261           63 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames.allocate(NumNumbers);
     262           63 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = "";
     263           63 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = cNumericFields;
     264           63 :             Util::IsNameEmpty(state, Alphas(1), CurrentModuleObject, ErrorsFound);
     265              : 
     266           63 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name = Alphas(1);
     267              : 
     268           63 :             if (lAlphaBlanks(2)) {
     269            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched = Sched::GetScheduleAlwaysOn(state);
     270           63 :             } else if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched = Sched::GetSchedule(state, Alphas(2))) == nullptr) {
     271            0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
     272            0 :                 ErrorsFound = true;
     273              :             }
     274              : 
     275              :             // Main air nodes (except outside air node):
     276           63 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode =
     277          126 :                 NodeInputManager::GetOnlySingleNode(state,
     278           63 :                                                     Alphas(3),
     279              :                                                     ErrorsFound,
     280              :                                                     DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
     281           63 :                                                     Alphas(1),
     282              :                                                     DataLoopNode::NodeFluidType::Air,
     283              :                                                     DataLoopNode::ConnectionType::Inlet,
     284              :                                                     NodeInputManager::CompFluidStream::Primary,
     285              :                                                     DataLoopNode::ObjectIsParent);
     286              : 
     287           63 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode =
     288          126 :                 NodeInputManager::GetOnlySingleNode(state,
     289           63 :                                                     Alphas(4),
     290              :                                                     ErrorsFound,
     291              :                                                     DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
     292           63 :                                                     Alphas(1),
     293              :                                                     DataLoopNode::NodeFluidType::Air,
     294              :                                                     DataLoopNode::ConnectionType::Outlet,
     295              :                                                     NodeInputManager::CompFluidStream::Primary,
     296              :                                                     DataLoopNode::ObjectIsParent);
     297              : 
     298           63 :             auto &unitHeat = state.dataUnitHeaters->UnitHeat(UnitHeatNum);
     299              :             // Fan information:
     300           63 :             unitHeat.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, Alphas(5)));
     301           63 :             if (unitHeat.fanType != HVAC::FanType::Constant && unitHeat.fanType != HVAC::FanType::VAV && unitHeat.fanType != HVAC::FanType::OnOff &&
     302            3 :                 unitHeat.fanType != HVAC::FanType::SystemModel) {
     303            0 :                 ShowSevereInvalidKey(state, eoh, cAlphaFields(5), Alphas(5), "Fan Type must be Fan:ConstantVolume, Fan:VariableVolume, or Fan:OnOff");
     304            0 :                 ErrorsFound = true;
     305              :             }
     306              : 
     307           63 :             unitHeat.FanName = Alphas(6);
     308           63 :             unitHeat.MaxAirVolFlow = Numbers(1);
     309              : 
     310           63 :             if ((unitHeat.Fan_Index = Fans::GetFanIndex(state, unitHeat.FanName)) == 0) {
     311            0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFields(6), unitHeat.FanName);
     312            0 :                 ErrorsFound = true;
     313              : 
     314              :             } else {
     315           63 :                 auto *fan = state.dataFans->fans(unitHeat.Fan_Index);
     316              : 
     317           63 :                 unitHeat.FanOutletNode = fan->outletNodeNum;
     318              : 
     319           63 :                 FanVolFlow = fan->maxAirFlowRate;
     320              : 
     321           63 :                 if (FanVolFlow != DataSizing::AutoSize && unitHeat.MaxAirVolFlow != DataSizing::AutoSize && FanVolFlow < unitHeat.MaxAirVolFlow) {
     322            0 :                     ShowSevereError(state, format("Specified in {} = {}", CurrentModuleObject, unitHeat.Name));
     323            0 :                     ShowContinueError(
     324              :                         state,
     325            0 :                         format("...air flow rate ({:.7T}) in fan object {} is less than the unit heater maximum supply air flow rate ({:.7T}).",
     326              :                                FanVolFlow,
     327            0 :                                unitHeat.FanName,
     328            0 :                                unitHeat.MaxAirVolFlow));
     329            0 :                     ShowContinueError(state, "...the fan flow rate must be greater than or equal to the unit heater maximum supply air flow rate.");
     330            0 :                     ErrorsFound = true;
     331           63 :                 } else if (FanVolFlow == DataSizing::AutoSize && unitHeat.MaxAirVolFlow != DataSizing::AutoSize) {
     332            0 :                     ShowWarningError(state, format("Specified in {} = {}", CurrentModuleObject, unitHeat.Name));
     333            0 :                     ShowContinueError(state, "...the fan flow rate is autosized while the unit heater flow rate is not.");
     334            0 :                     ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
     335           63 :                 } else if (FanVolFlow != DataSizing::AutoSize && unitHeat.MaxAirVolFlow == DataSizing::AutoSize) {
     336            0 :                     ShowWarningError(state, format("Specified in {} = {}", CurrentModuleObject, unitHeat.Name));
     337            0 :                     ShowContinueError(state, "...the unit heater flow rate is autosized while the fan flow rate is not.");
     338            0 :                     ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
     339              :                 }
     340           63 :                 unitHeat.fanAvailSched = fan->availSched;
     341              :             }
     342              : 
     343              :             // Heating coil information:
     344              :             {
     345           63 :                 unitHeat.Type = static_cast<HCoilType>(getEnumValue(HCoilTypeNamesUC, Util::makeUPPER(Alphas(7))));
     346           63 :                 switch (unitHeat.Type) {
     347            6 :                 case HCoilType::WaterHeatingCoil:
     348            6 :                     unitHeat.HeatingCoilType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
     349            6 :                     break;
     350            0 :                 case HCoilType::SteamCoil:
     351            0 :                     unitHeat.HeatingCoilType = DataPlant::PlantEquipmentType::CoilSteamAirHeating;
     352            0 :                     break;
     353           57 :                 case HCoilType::Electric:
     354              :                 case HCoilType::Gas:
     355           57 :                     break;
     356            0 :                 default: {
     357            0 :                     ShowSevereError(state, format("Illegal {} = {}", cAlphaFields(7), Alphas(7)));
     358            0 :                     ShowContinueError(state, format("Occurs in {}={}", CurrentModuleObject, unitHeat.Name));
     359            0 :                     ErrorsFound = true;
     360            0 :                     errFlag = true;
     361              :                 }
     362              :                 }
     363              :             }
     364              : 
     365           63 :             if (!errFlag) {
     366           63 :                 unitHeat.HCoilTypeCh = Alphas(7);
     367           63 :                 unitHeat.HCoilName = Alphas(8);
     368           63 :                 ValidateComponent(state, Alphas(7), unitHeat.HCoilName, IsNotOK, CurrentModuleObject);
     369           63 :                 if (IsNotOK) {
     370            0 :                     ShowContinueError(state, format("specified in {} = \"{}\"", CurrentModuleObject, unitHeat.Name));
     371            0 :                     ErrorsFound = true;
     372              :                 } else {
     373              :                     // The heating coil control node is necessary for hot water and steam coils, but not necessary for an
     374              :                     // electric or gas coil.
     375           63 :                     if (unitHeat.Type == HCoilType::WaterHeatingCoil || unitHeat.Type == HCoilType::SteamCoil) {
     376              :                         // mine the hot water or steam node from the coil object
     377            6 :                         errFlag = false;
     378            6 :                         if (unitHeat.Type == HCoilType::WaterHeatingCoil) {
     379            6 :                             unitHeat.HotControlNode = WaterCoils::GetCoilWaterInletNode(state, "Coil:Heating:Water", unitHeat.HCoilName, errFlag);
     380              :                         } else { // its a steam coil
     381            0 :                             unitHeat.HCoil_Index = SteamCoils::GetSteamCoilIndex(state, "COIL:HEATING:STEAM", unitHeat.HCoilName, errFlag);
     382            0 :                             unitHeat.HotControlNode = SteamCoils::GetCoilSteamInletNode(state, unitHeat.HCoil_Index, unitHeat.HCoilName, errFlag);
     383            0 :                             unitHeat.HCoil_fluid = Fluid::GetSteam(state);
     384              :                         }
     385              :                         // Other error checks should trap before it gets to this point in the code, but including just in case.
     386            6 :                         if (errFlag) {
     387            0 :                             ShowContinueError(state, format("that was specified in {} = \"{}\"", CurrentModuleObject, unitHeat.Name));
     388            0 :                             ErrorsFound = true;
     389              :                         }
     390              :                     }
     391              :                 }
     392              :             }
     393              : 
     394           63 :             if (lAlphaBlanks(9)) {
     395          180 :                 unitHeat.fanOp = (unitHeat.fanType == HVAC::FanType::OnOff || unitHeat.fanType == HVAC::FanType::SystemModel)
     396           60 :                                      ? HVAC::FanOp::Cycling
     397              :                                      : HVAC::FanOp::Continuous;
     398            3 :             } else if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched = Sched::GetSchedule(state, Alphas(9))) == nullptr) {
     399            0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFields(9), Alphas(9));
     400            0 :                 ErrorsFound = true;
     401            3 :             } else if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanType == HVAC::FanType::Constant &&
     402            0 :                        !state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
     403            0 :                 Sched::ShowSevereBadMinMax(state, eoh, cAlphaFields(9), Alphas(9), Clusive::In, 0.0, Clusive::In, 1.0);
     404            0 :                 ErrorsFound = true;
     405              :             }
     406              : 
     407           63 :             unitHeat.FanOperatesDuringNoHeating = Alphas(10);
     408           63 :             if ((!Util::SameString(unitHeat.FanOperatesDuringNoHeating, "Yes")) && (!Util::SameString(unitHeat.FanOperatesDuringNoHeating, "No"))) {
     409            0 :                 ErrorsFound = true;
     410            0 :                 ShowSevereError(state, format("Illegal {} = {}", cAlphaFields(10), Alphas(10)));
     411            0 :                 ShowContinueError(state, format("Occurs in {}={}", CurrentModuleObject, unitHeat.Name));
     412           63 :             } else if (Util::SameString(unitHeat.FanOperatesDuringNoHeating, "No")) {
     413           44 :                 unitHeat.FanOffNoHeating = true;
     414              :             }
     415              : 
     416           63 :             unitHeat.MaxVolHotWaterFlow = Numbers(2);
     417           63 :             unitHeat.MinVolHotWaterFlow = Numbers(3);
     418           63 :             unitHeat.MaxVolHotSteamFlow = Numbers(2);
     419           63 :             unitHeat.MinVolHotSteamFlow = Numbers(3);
     420              : 
     421           63 :             unitHeat.HotControlOffset = Numbers(4);
     422              :             // Set default convergence tolerance
     423           63 :             if (unitHeat.HotControlOffset <= 0.0) {
     424            0 :                 unitHeat.HotControlOffset = 0.001;
     425              :             }
     426              : 
     427           63 :             if (!lAlphaBlanks(11)) {
     428            0 :                 unitHeat.AvailManagerListName = Alphas(11);
     429              :             }
     430              : 
     431           63 :             unitHeat.HVACSizingIndex = 0;
     432           63 :             if (!lAlphaBlanks(12)) {
     433            0 :                 unitHeat.HVACSizingIndex = Util::FindItemInList(Alphas(12), state.dataSize->ZoneHVACSizing);
     434            0 :                 if (unitHeat.HVACSizingIndex == 0) {
     435            0 :                     ShowSevereError(state, format("{} = {} not found.", cAlphaFields(12), Alphas(12)));
     436            0 :                     ShowContinueError(state, format("Occurs in {} = {}", CurrentModuleObject, unitHeat.Name));
     437            0 :                     ErrorsFound = true;
     438              :                 }
     439              :             }
     440              : 
     441              :             // check that unit heater air inlet node must be the same as a zone exhaust node
     442           63 :             bool ZoneNodeNotFound = true;
     443         3017 :             for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     444         2954 :                 if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     445          245 :                     continue;
     446              :                 }
     447         4780 :                 for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumExhaustNodes; ++NodeNum) {
     448         2134 :                     if (unitHeat.AirInNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ExhaustNode(NodeNum)) {
     449           63 :                         ZoneNodeNotFound = false;
     450           63 :                         break;
     451              :                     }
     452              :                 }
     453              :             }
     454           63 :             if (ZoneNodeNotFound) {
     455            0 :                 ShowSevereError(state,
     456            0 :                                 format("{} = \"{}\". Unit heater air inlet node name must be the same as a zone exhaust node name.",
     457              :                                        CurrentModuleObject,
     458            0 :                                        unitHeat.Name));
     459            0 :                 ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
     460            0 :                 ShowContinueError(state, format("..Unit heater air inlet node name = {}", state.dataLoopNodes->NodeID(unitHeat.AirInNode)));
     461            0 :                 ErrorsFound = true;
     462              :             }
     463              :             // check that unit heater air outlet node is a zone inlet node.
     464           63 :             ZoneNodeNotFound = true;
     465         3017 :             for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     466         2954 :                 if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     467          245 :                     continue;
     468              :                 }
     469         5341 :                 for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++NodeNum) {
     470         2695 :                     if (unitHeat.AirOutNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(NodeNum)) {
     471           63 :                         unitHeat.ZonePtr = CtrlZone;
     472           63 :                         ZoneNodeNotFound = false;
     473           63 :                         break;
     474              :                     }
     475              :                 }
     476              :             }
     477           63 :             if (ZoneNodeNotFound) {
     478            0 :                 ShowSevereError(state,
     479            0 :                                 format("{} = \"{}\". Unit heater air outlet node name must be the same as a zone inlet node name.",
     480              :                                        CurrentModuleObject,
     481            0 :                                        unitHeat.Name));
     482            0 :                 ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
     483            0 :                 ShowContinueError(state, format("..Unit heater air outlet node name = {}", state.dataLoopNodes->NodeID(unitHeat.AirOutNode)));
     484            0 :                 ErrorsFound = true;
     485              :             }
     486              : 
     487              :             // Add fan to component sets array
     488          189 :             BranchNodeConnections::SetUpCompSets(state,
     489              :                                                  CurrentModuleObject,
     490              :                                                  unitHeat.Name,
     491           63 :                                                  HVAC::fanTypeNamesUC[(int)unitHeat.fanType],
     492              :                                                  unitHeat.FanName,
     493           63 :                                                  state.dataLoopNodes->NodeID(unitHeat.AirInNode),
     494           63 :                                                  state.dataLoopNodes->NodeID(unitHeat.FanOutletNode));
     495              : 
     496              :             // Add heating coil to component sets array
     497          126 :             BranchNodeConnections::SetUpCompSets(state,
     498              :                                                  CurrentModuleObject,
     499              :                                                  unitHeat.Name,
     500              :                                                  unitHeat.HCoilTypeCh,
     501              :                                                  unitHeat.HCoilName,
     502           63 :                                                  state.dataLoopNodes->NodeID(unitHeat.FanOutletNode),
     503           63 :                                                  state.dataLoopNodes->NodeID(unitHeat.AirOutNode));
     504              : 
     505              :         } // ...loop over all of the unit heaters found in the input file
     506              : 
     507           14 :         Alphas.deallocate();
     508           14 :         Numbers.deallocate();
     509           14 :         cAlphaFields.deallocate();
     510           14 :         cNumericFields.deallocate();
     511           14 :         lAlphaBlanks.deallocate();
     512           14 :         lNumericBlanks.deallocate();
     513              : 
     514           14 :         if (ErrorsFound) {
     515            0 :             ShowFatalError(state, format("{}Errors found in input", RoutineName));
     516              :         }
     517              : 
     518              :         // Setup Report variables for the Unit Heaters, CurrentModuleObject='ZoneHVAC:UnitHeater'
     519           77 :         for (int UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
     520           63 :             auto &unitHeat = state.dataUnitHeaters->UnitHeat(UnitHeatNum);
     521          126 :             SetupOutputVariable(state,
     522              :                                 "Zone Unit Heater Heating Rate",
     523              :                                 Constant::Units::W,
     524           63 :                                 unitHeat.HeatPower,
     525              :                                 OutputProcessor::TimeStepType::System,
     526              :                                 OutputProcessor::StoreType::Average,
     527           63 :                                 unitHeat.Name);
     528          126 :             SetupOutputVariable(state,
     529              :                                 "Zone Unit Heater Heating Energy",
     530              :                                 Constant::Units::J,
     531           63 :                                 unitHeat.HeatEnergy,
     532              :                                 OutputProcessor::TimeStepType::System,
     533              :                                 OutputProcessor::StoreType::Sum,
     534           63 :                                 unitHeat.Name);
     535          126 :             SetupOutputVariable(state,
     536              :                                 "Zone Unit Heater Fan Electricity Rate",
     537              :                                 Constant::Units::W,
     538           63 :                                 unitHeat.ElecPower,
     539              :                                 OutputProcessor::TimeStepType::System,
     540              :                                 OutputProcessor::StoreType::Average,
     541           63 :                                 unitHeat.Name);
     542              :             // Note that the unit heater fan electric is NOT metered because this value is already metered through the fan component
     543          126 :             SetupOutputVariable(state,
     544              :                                 "Zone Unit Heater Fan Electricity Energy",
     545              :                                 Constant::Units::J,
     546           63 :                                 unitHeat.ElecEnergy,
     547              :                                 OutputProcessor::TimeStepType::System,
     548              :                                 OutputProcessor::StoreType::Sum,
     549           63 :                                 unitHeat.Name);
     550           63 :             SetupOutputVariable(state,
     551              :                                 "Zone Unit Heater Fan Availability Status",
     552              :                                 Constant::Units::None,
     553           63 :                                 (int &)unitHeat.availStatus,
     554              :                                 OutputProcessor::TimeStepType::System,
     555              :                                 OutputProcessor::StoreType::Average,
     556           63 :                                 unitHeat.Name);
     557           63 :             if (unitHeat.fanType == HVAC::FanType::OnOff) {
     558            0 :                 SetupOutputVariable(state,
     559              :                                     "Zone Unit Heater Fan Part Load Ratio",
     560              :                                     Constant::Units::None,
     561            0 :                                     unitHeat.FanPartLoadRatio,
     562              :                                     OutputProcessor::TimeStepType::System,
     563              :                                     OutputProcessor::StoreType::Average,
     564            0 :                                     unitHeat.Name);
     565              :             }
     566           63 :             state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(
     567           63 :                 state, unitHeat.HCoilName, unitHeat.HCoilTypeCh, unitHeat.FanName, unitHeat.fanType, unitHeat.Fan_Index);
     568              :         }
     569           14 :     }
     570              : 
     571       638585 :     void InitUnitHeater(EnergyPlusData &state,
     572              :                         int const UnitHeatNum,                         // index for the current unit heater
     573              :                         int const ZoneNum,                             // number of zone being served
     574              :                         [[maybe_unused]] bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
     575              :     )
     576              :     {
     577              : 
     578              :         // SUBROUTINE INFORMATION:
     579              :         //       AUTHOR         Rick Strand
     580              :         //       DATE WRITTEN   May 2000
     581              :         //       MODIFIED       Chandan Sharma, FSEC, March 2011: Added ZoneHVAC sys avail manager
     582              : 
     583              :         // PURPOSE OF THIS SUBROUTINE:
     584              :         // This subroutine initializes all of the data elements which are necessary
     585              :         // to simulate a unit heater.
     586              : 
     587              :         // METHODOLOGY EMPLOYED:
     588              :         // Uses the status flags to trigger initializations.
     589              : 
     590              :         // SUBROUTINE PARAMETER DEFINITIONS:
     591              :         static constexpr std::string_view RoutineName("InitUnitHeater");
     592              : 
     593              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     594              :         int InNode;    // inlet node number in unit heater loop
     595              :         int OutNode;   // outlet node number in unit heater loop
     596              :         Real64 RhoAir; // air density at InNode
     597              :         Real64 TempSteamIn;
     598              :         Real64 SteamDensity;
     599              :         Real64 rho; // local fluid density
     600              : 
     601              :         // Do the one time initializations
     602       638585 :         if (state.dataUnitHeaters->InitUnitHeaterOneTimeFlag) {
     603              : 
     604           14 :             state.dataUnitHeaters->MyEnvrnFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     605           14 :             state.dataUnitHeaters->MySizeFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     606           14 :             state.dataUnitHeaters->MyPlantScanFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     607           14 :             state.dataUnitHeaters->MyZoneEqFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     608           14 :             state.dataUnitHeaters->MyEnvrnFlag = true;
     609           14 :             state.dataUnitHeaters->MySizeFlag = true;
     610           14 :             state.dataUnitHeaters->MyPlantScanFlag = true;
     611           14 :             state.dataUnitHeaters->MyZoneEqFlag = true;
     612           14 :             state.dataUnitHeaters->InitUnitHeaterOneTimeFlag = false;
     613              :         }
     614              : 
     615       638585 :         if (allocated(state.dataAvail->ZoneComp)) {
     616       638581 :             auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::UnitHeater).ZoneCompAvailMgrs(UnitHeatNum);
     617       638581 :             if (state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum)) { // initialize the name of each availability manager list and zone number
     618           63 :                 availMgr.AvailManagerListName = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailManagerListName;
     619           63 :                 availMgr.ZoneNum = ZoneNum;
     620           63 :                 state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum) = false;
     621              :             }
     622       638581 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).availStatus = availMgr.availStatus;
     623              :         }
     624              : 
     625       638585 :         if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && allocated(state.dataPlnt->PlantLoop)) {
     626          120 :             if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
     627           57 :                 (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
     628            6 :                 bool errFlag = false;
     629           12 :                 PlantUtilities::ScanPlantLoopsForObject(state,
     630            6 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
     631            6 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType,
     632            6 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc,
     633              :                                                         errFlag,
     634              :                                                         _,
     635              :                                                         _,
     636              :                                                         _,
     637              :                                                         _,
     638              :                                                         _);
     639            6 :                 if (errFlag) {
     640            0 :                     ShowContinueError(state,
     641            0 :                                       format("Reference Unit=\"{}\", type=ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
     642            0 :                     ShowFatalError(state, "InitUnitHeater: Program terminated due to previous condition(s).");
     643              :                 }
     644              : 
     645            6 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum =
     646            6 :                     DataPlant::CompData::getPlantComponent(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc).NodeNumOut;
     647              :             }
     648           63 :             state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
     649       638522 :         } else if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && !state.dataGlobal->AnyPlantInModel) {
     650            0 :             state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
     651              :         }
     652              :         // need to check all units to see if they are on Zone Equipment List or issue warning
     653       638585 :         if (!state.dataUnitHeaters->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
     654           14 :             state.dataUnitHeaters->ZoneEquipmentListChecked = true;
     655           77 :             for (int Loop = 1; Loop <= state.dataUnitHeaters->NumOfUnitHeats; ++Loop) {
     656           63 :                 if (DataZoneEquipment::CheckZoneEquipmentList(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(Loop).Name)) {
     657           63 :                     continue;
     658              :                 }
     659            0 :                 ShowSevereError(state,
     660            0 :                                 format("InitUnitHeater: Unit=[UNIT HEATER,{}] is not on any ZoneHVAC:EquipmentList.  It will not be simulated.",
     661            0 :                                        state.dataUnitHeaters->UnitHeat(Loop).Name));
     662              :             }
     663              :         }
     664              : 
     665       638648 :         if (!state.dataGlobal->SysSizingCalc && state.dataUnitHeaters->MySizeFlag(UnitHeatNum) &&
     666           63 :             !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
     667              : 
     668           63 :             SizeUnitHeater(state, UnitHeatNum);
     669              : 
     670           63 :             state.dataUnitHeaters->MySizeFlag(UnitHeatNum) = false;
     671              :         } // Do the one time initializations
     672              : 
     673       638926 :         if (state.dataGlobal->BeginEnvrnFlag && state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) &&
     674          341 :             !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
     675          341 :             InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
     676          341 :             OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
     677          341 :             RhoAir = state.dataEnvrn->StdRhoAir;
     678              : 
     679              :             // set the mass flow rates from the input volume flow rates
     680          341 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow = RhoAir * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
     681              : 
     682              :             // set the node max and min mass flow rates
     683          341 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     684          341 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
     685              : 
     686          341 :             state.dataLoopNodes->Node(InNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     687          341 :             state.dataLoopNodes->Node(InNode).MassFlowRateMin = 0.0;
     688              : 
     689          341 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
     690           30 :                 rho = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
     691           30 :                           .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     692              : 
     693           30 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
     694           30 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotWaterFlow;
     695          120 :                 PlantUtilities::InitComponentNodes(state,
     696           30 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow,
     697           30 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow,
     698           30 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
     699           30 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
     700              :             }
     701          341 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
     702            0 :                 TempSteamIn = 100.00;
     703            0 :                 SteamDensity = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_fluid->getSatDensity(state, TempSteamIn, 1.0, RoutineName);
     704            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow =
     705            0 :                     SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
     706            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow =
     707            0 :                     SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotSteamFlow;
     708              : 
     709            0 :                 PlantUtilities::InitComponentNodes(state,
     710            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow,
     711            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow,
     712            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
     713            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
     714              :             }
     715              : 
     716          341 :             state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = false;
     717              :         } // ...end start of environment inits
     718              : 
     719       638585 :         if (!state.dataGlobal->BeginEnvrnFlag) {
     720       634773 :             state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = true;
     721              :         }
     722              : 
     723              :         // These initializations are done every iteration...
     724       638585 :         InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
     725       638585 :         OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
     726              : 
     727       638585 :         state.dataUnitHeaters->QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // zone load needed
     728       638585 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched != nullptr) {
     729        13320 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched->getCurrentVal() == 0.0 &&
     730            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanType == HVAC::FanType::OnOff) {
     731            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Cycling;
     732              :             } else {
     733        13320 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Continuous;
     734              :             }
     735        13320 :             if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
     736              :                 // Unit is available, but there is no load on it or we are in setback/deadband
     737        12114 :                 if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
     738         4842 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched->getCurrentVal() > 0.0) {
     739         4842 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Continuous;
     740              :                 }
     741              :             }
     742              :         }
     743              : 
     744       638585 :         state.dataUnitHeaters->SetMassFlowRateToZero = false;
     745       638585 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() > 0) {
     746      1178350 :             if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() > 0 || state.dataHVACGlobal->TurnFansOn) &&
     747       583625 :                 !state.dataHVACGlobal->TurnFansOff) {
     748       996331 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
     749       412706 :                     ((state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP < HVAC::SmallLoad) ||
     750       161708 :                      (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)))) {
     751       251224 :                     state.dataUnitHeaters->SetMassFlowRateToZero = true;
     752              :                 }
     753              :             } else {
     754        11100 :                 state.dataUnitHeaters->SetMassFlowRateToZero = true;
     755              :             }
     756              :         } else {
     757        43860 :             state.dataUnitHeaters->SetMassFlowRateToZero = true;
     758              :         }
     759              : 
     760       638585 :         if (state.dataUnitHeaters->SetMassFlowRateToZero) {
     761       306184 :             state.dataLoopNodes->Node(InNode).MassFlowRate = 0.0;
     762       306184 :             state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = 0.0;
     763       306184 :             state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = 0.0;
     764       306184 :             state.dataLoopNodes->Node(OutNode).MassFlowRate = 0.0;
     765       306184 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = 0.0;
     766       306184 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = 0.0;
     767              :         } else {
     768       332401 :             state.dataLoopNodes->Node(InNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     769       332401 :             state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     770       332401 :             state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     771       332401 :             state.dataLoopNodes->Node(OutNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     772       332401 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     773       332401 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     774              :         }
     775              : 
     776              :         // Just in case the unit is off and conditions do not get sent through
     777              :         // the unit for some reason, set the outlet conditions equal to the inlet
     778              :         // conditions of the unit heater
     779       638585 :         state.dataLoopNodes->Node(OutNode).Temp = state.dataLoopNodes->Node(InNode).Temp;
     780       638585 :         state.dataLoopNodes->Node(OutNode).Press = state.dataLoopNodes->Node(InNode).Press;
     781       638585 :         state.dataLoopNodes->Node(OutNode).HumRat = state.dataLoopNodes->Node(InNode).HumRat;
     782       638585 :         state.dataLoopNodes->Node(OutNode).Enthalpy = state.dataLoopNodes->Node(InNode).Enthalpy;
     783       638585 :     }
     784              : 
     785           63 :     void SizeUnitHeater(EnergyPlusData &state, int const UnitHeatNum)
     786              :     {
     787              : 
     788              :         // SUBROUTINE INFORMATION:
     789              :         //       AUTHOR         Fred Buhl
     790              :         //       DATE WRITTEN   February 2002
     791              :         //       MODIFIED       August 2013 Daeho Kang, add component sizing table entries
     792              :         //                      July 2014, B. Nigusse, added scalable sizing
     793              :         //       RE-ENGINEERED  na
     794              : 
     795              :         // PURPOSE OF THIS SUBROUTINE:
     796              :         // This subroutine is for sizing Unit Heater components for which flow rates have not been
     797              :         // specified in the input.
     798              : 
     799              :         // METHODOLOGY EMPLOYED:
     800              :         // Obtains flow rates from the zone sizing arrays and plant sizing data.
     801              : 
     802              :         // SUBROUTINE PARAMETER DEFINITIONS:
     803              :         static constexpr std::string_view RoutineName("SizeUnitHeater");
     804              : 
     805              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     806              :         int PltSizHeatNum; // index of plant sizing object for 1st heating loop
     807              :         Real64 DesCoilLoad;
     808              :         Real64 TempSteamIn;
     809              :         Real64 EnthSteamInDry;
     810              :         Real64 EnthSteamOutWet;
     811              :         Real64 LatentHeatSteam;
     812              :         Real64 SteamDensity;
     813              :         Real64 Cp;                 // local temporary for fluid specific heat
     814              :         Real64 rho;                // local temporary for fluid density
     815           63 :         std::string SizingString;  // input field sizing description (e.g., Nominal Capacity)
     816              :         Real64 TempSize;           // autosized value of coil input field
     817              :         bool PrintFlag;            // TRUE when sizing information is reported in the eio file
     818              :         int zoneHVACIndex;         // index of zoneHVAC equipment sizing specification
     819              :         Real64 WaterCoilSizDeltaT; // water coil deltaT for design water flow rate autosizing
     820              : 
     821           63 :         int &CurZoneEqNum = state.dataSize->CurZoneEqNum;
     822              : 
     823           63 :         bool ErrorsFound = false;
     824           63 :         Real64 MaxAirVolFlowDes = 0.0;
     825           63 :         Real64 MaxAirVolFlowUser = 0.0;
     826           63 :         Real64 MaxVolHotWaterFlowDes = 0.0;
     827           63 :         Real64 MaxVolHotWaterFlowUser = 0.0;
     828           63 :         Real64 MaxVolHotSteamFlowDes = 0.0;
     829           63 :         Real64 MaxVolHotSteamFlowUser = 0.0;
     830              : 
     831           63 :         state.dataSize->DataScalableSizingON = false;
     832           63 :         state.dataSize->DataScalableCapSizingON = false;
     833           63 :         state.dataSize->ZoneHeatingOnlyFan = true;
     834           63 :         std::string CompType = "ZoneHVAC:UnitHeater";
     835           63 :         std::string CompName = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name;
     836           63 :         state.dataSize->DataZoneNumber = state.dataUnitHeaters->UnitHeat(UnitHeatNum).ZonePtr;
     837           63 :         state.dataSize->DataFanType = state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanType;
     838           63 :         state.dataSize->DataFanIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index;
     839              :         // unit heater is always blow thru
     840           63 :         state.dataSize->DataFanPlacement = HVAC::FanPlace::BlowThru;
     841              : 
     842           63 :         if (CurZoneEqNum > 0) {
     843           63 :             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
     844           63 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
     845            0 :                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
     846            0 :                 int SizingMethod = HVAC::HeatingAirflowSizing;
     847            0 :                 int FieldNum = 1; //  N1 , \field Maximum Supply Air Flow Rate
     848            0 :                 PrintFlag = true;
     849            0 :                 SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
     850            0 :                 int SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
     851            0 :                 ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
     852            0 :                 if (SAFMethod == DataSizing::None || SAFMethod == DataSizing::SupplyAirFlowRate || SAFMethod == DataSizing::FlowPerFloorArea ||
     853              :                     SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
     854            0 :                     if (SAFMethod == DataSizing::SupplyAirFlowRate) {
     855            0 :                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
     856            0 :                             ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     857            0 :                             ZoneEqSizing.SystemAirFlow = true;
     858              :                         }
     859            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     860            0 :                     } else if (SAFMethod == DataSizing::FlowPerFloorArea) {
     861            0 :                         ZoneEqSizing.SystemAirFlow = true;
     862            0 :                         ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
     863            0 :                                                   state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
     864            0 :                         TempSize = ZoneEqSizing.AirVolFlow;
     865            0 :                         state.dataSize->DataScalableSizingON = true;
     866            0 :                     } else if (SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
     867            0 :                         state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     868            0 :                         TempSize = DataSizing::AutoSize;
     869            0 :                         state.dataSize->DataScalableSizingON = true;
     870              :                     } else {
     871            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     872              :                     }
     873            0 :                     bool errorsFound = false;
     874            0 :                     HeatingAirFlowSizer sizingHeatingAirFlow;
     875            0 :                     sizingHeatingAirFlow.overrideSizingString(SizingString);
     876              :                     // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     877            0 :                     sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     878            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     879              : 
     880            0 :                 } else if (SAFMethod == DataSizing::FlowPerHeatingCapacity) {
     881            0 :                     TempSize = DataSizing::AutoSize;
     882            0 :                     PrintFlag = false;
     883            0 :                     state.dataSize->DataScalableSizingON = true;
     884            0 :                     state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatVolFlow;
     885            0 :                     bool errorsFound = false;
     886            0 :                     HeatingCapacitySizer sizerHeatingCapacity;
     887            0 :                     sizerHeatingCapacity.overrideSizingString(SizingString);
     888            0 :                     sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     889            0 :                     TempSize = sizerHeatingCapacity.size(state, TempSize, errorsFound);
     890            0 :                     if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     891            0 :                         state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     892              :                     }
     893            0 :                     state.dataSize->DataAutosizedHeatingCapacity = TempSize;
     894            0 :                     state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     895            0 :                     PrintFlag = true;
     896            0 :                     TempSize = DataSizing::AutoSize;
     897            0 :                     errorsFound = false;
     898            0 :                     HeatingAirFlowSizer sizingHeatingAirFlow;
     899            0 :                     sizingHeatingAirFlow.overrideSizingString(SizingString);
     900              :                     // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     901            0 :                     sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     902            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     903            0 :                 }
     904            0 :                 state.dataSize->DataScalableSizingON = false;
     905              :             } else {
     906              :                 // no scalble sizing method has been specified. Sizing proceeds using the method
     907              :                 // specified in the zoneHVAC object
     908           63 :                 int FieldNum = 1; // N1 , \field Maximum Supply Air Flow Rate
     909           63 :                 PrintFlag = true;
     910           63 :                 SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
     911           63 :                 TempSize = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
     912           63 :                 bool errorsFound = false;
     913           63 :                 HeatingAirFlowSizer sizingHeatingAirFlow;
     914           63 :                 sizingHeatingAirFlow.overrideSizingString(SizingString);
     915              :                 // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     916           63 :                 sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     917           63 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     918           63 :             }
     919              :         }
     920              : 
     921           63 :         bool IsAutoSize = false;
     922           63 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow == DataSizing::AutoSize) {
     923           43 :             IsAutoSize = true;
     924              :         }
     925              : 
     926           63 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
     927              : 
     928            6 :             if (CurZoneEqNum > 0) {
     929            6 :                 if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
     930            3 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0) {
     931            6 :                         BaseSizer::reportSizerOutput(state,
     932              :                                                      "ZoneHVAC:UnitHeater",
     933            3 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
     934              :                                                      "User-Specified Maximum Hot Water Flow [m3/s]",
     935            3 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow);
     936              :                     }
     937              :                 } else {
     938            3 :                     CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
     939              : 
     940            3 :                     int CoilWaterInletNode = WaterCoils::GetCoilWaterInletNode(
     941            3 :                         state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     942            3 :                     int CoilWaterOutletNode = WaterCoils::GetCoilWaterOutletNode(
     943            3 :                         state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     944            3 :                     if (IsAutoSize) {
     945            3 :                         bool DoWaterCoilSizing = false; // if TRUE do water coil sizing calculation
     946            3 :                         PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(state,
     947              :                                                                            "Coil:Heating:Water",
     948            3 :                                                                            state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
     949              :                                                                            CoilWaterInletNode,
     950              :                                                                            CoilWaterOutletNode,
     951              :                                                                            ErrorsFound);
     952            3 :                         int CoilNum = WaterCoils::GetWaterCoilIndex(
     953            3 :                             state, "COIL:HEATING:WATER", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     954            3 :                         if (state.dataWaterCoils->WaterCoil(CoilNum).UseDesignWaterDeltaTemp) {
     955            0 :                             WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(CoilNum).DesignWaterDeltaTemp;
     956            0 :                             DoWaterCoilSizing = true;
     957              :                         } else {
     958            3 :                             if (PltSizHeatNum > 0) {
     959            3 :                                 WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizHeatNum).DeltaT;
     960            3 :                                 DoWaterCoilSizing = true;
     961              :                             } else {
     962            0 :                                 DoWaterCoilSizing = false;
     963              :                                 // If there is no heating Plant Sizing object and autosizing was requested, issue fatal error message
     964            0 :                                 ShowSevereError(state, "Autosizing of water coil requires a heating loop Sizing:Plant object");
     965            0 :                                 ShowContinueError(
     966            0 :                                     state, format("Occurs in ZoneHVAC:UnitHeater Object={}", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
     967            0 :                                 ErrorsFound = true;
     968              :                             }
     969              :                         }
     970              : 
     971            3 :                         if (DoWaterCoilSizing) {
     972            3 :                             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
     973            3 :                             int SizingMethod = HVAC::HeatingCapacitySizing;
     974            3 :                             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
     975            0 :                                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
     976            0 :                                 int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
     977            0 :                                 ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
     978            0 :                                 if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
     979              :                                     CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     980            0 :                                     if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
     981            0 :                                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == DataSizing::AutoSize) {
     982            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
     983              :                                         } else {
     984            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     985              :                                         }
     986            0 :                                         ZoneEqSizing.HeatingCapacity = true;
     987            0 :                                         TempSize = DataSizing::AutoSize;
     988            0 :                                     } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
     989            0 :                                         ZoneEqSizing.HeatingCapacity = true;
     990            0 :                                         ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
     991            0 :                                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
     992            0 :                                         state.dataSize->DataScalableCapSizingON = true;
     993            0 :                                     } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     994            0 :                                         state.dataSize->DataFracOfAutosizedHeatingCapacity =
     995            0 :                                             state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     996            0 :                                         state.dataSize->DataScalableCapSizingON = true;
     997            0 :                                         TempSize = DataSizing::AutoSize;
     998              :                                     }
     999              :                                 }
    1000            0 :                                 PrintFlag = false;
    1001            0 :                                 bool errorsFound = false;
    1002            0 :                                 HeatingCapacitySizer sizerHeatingCapacity;
    1003            0 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1004            0 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1005            0 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
    1006            0 :                                 state.dataSize->DataScalableCapSizingON = false;
    1007            0 :                             } else {
    1008            3 :                                 SizingString = "";
    1009            3 :                                 PrintFlag = false;
    1010            3 :                                 TempSize = DataSizing::AutoSize;
    1011            3 :                                 ZoneEqSizing.HeatingCapacity = true;
    1012            3 :                                 ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1013            3 :                                 bool errorsFound = false;
    1014            3 :                                 HeatingCapacitySizer sizerHeatingCapacity;
    1015            3 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1016            3 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1017            3 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
    1018            3 :                             }
    1019              : 
    1020            3 :                             if (DesCoilLoad >= HVAC::SmallLoad) {
    1021            3 :                                 rho = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
    1022            3 :                                           .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
    1023            3 :                                 Cp = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
    1024            3 :                                          .glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineName);
    1025            3 :                                 MaxVolHotWaterFlowDes = DesCoilLoad / (WaterCoilSizDeltaT * Cp * rho);
    1026              :                             } else {
    1027            0 :                                 MaxVolHotWaterFlowDes = 0.0;
    1028              :                             }
    1029              :                         }
    1030            3 :                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
    1031            6 :                         BaseSizer::reportSizerOutput(state,
    1032              :                                                      "ZoneHVAC:UnitHeater",
    1033            3 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1034              :                                                      "Design Size Maximum Hot Water Flow [m3/s]",
    1035              :                                                      MaxVolHotWaterFlowDes);
    1036              :                     } else {
    1037            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
    1038            0 :                             MaxVolHotWaterFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
    1039            0 :                             BaseSizer::reportSizerOutput(state,
    1040              :                                                          "ZoneHVAC:UnitHeater",
    1041            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1042              :                                                          "Design Size Maximum Hot Water Flow [m3/s]",
    1043              :                                                          MaxVolHotWaterFlowDes,
    1044              :                                                          "User-Specified Maximum Hot Water Flow [m3/s]",
    1045              :                                                          MaxVolHotWaterFlowUser);
    1046            0 :                             if (state.dataGlobal->DisplayExtraWarnings) {
    1047            0 :                                 if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
    1048            0 :                                     state.dataSize->AutoVsHardSizingThreshold) {
    1049            0 :                                     ShowMessage(state,
    1050            0 :                                                 format("SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater {}",
    1051            0 :                                                        state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1052            0 :                                     ShowContinueError(state,
    1053            0 :                                                       format("User-Specified Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
    1054            0 :                                     ShowContinueError(
    1055            0 :                                         state, format("differs from Design Size Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
    1056            0 :                                     ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1057            0 :                                     ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1058              :                                 }
    1059              :                             }
    1060              :                         }
    1061              :                     }
    1062              :                 }
    1063              :             }
    1064              :         } else {
    1065           57 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = 0.0;
    1066              :         }
    1067              : 
    1068           63 :         IsAutoSize = false;
    1069           63 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow == DataSizing::AutoSize) {
    1070           43 :             IsAutoSize = true;
    1071              :         }
    1072              : 
    1073           63 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1074              : 
    1075            0 :             if (CurZoneEqNum > 0) {
    1076            0 :                 if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1077            0 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0) {
    1078            0 :                         BaseSizer::reportSizerOutput(state,
    1079              :                                                      "ZoneHVAC:UnitHeater",
    1080            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1081              :                                                      "User-Specified Maximum Steam Flow [m3/s]",
    1082            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow);
    1083              :                     }
    1084              :                 } else {
    1085            0 :                     auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
    1086            0 :                     CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
    1087              : 
    1088            0 :                     int CoilSteamInletNode = SteamCoils::GetCoilSteamInletNode(
    1089            0 :                         state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
    1090            0 :                     int CoilSteamOutletNode = SteamCoils::GetCoilSteamInletNode(
    1091            0 :                         state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
    1092            0 :                     if (IsAutoSize) {
    1093            0 :                         PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(state,
    1094              :                                                                            "Coil:Heating:Steam",
    1095            0 :                                                                            state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1096              :                                                                            CoilSteamInletNode,
    1097              :                                                                            CoilSteamOutletNode,
    1098              :                                                                            ErrorsFound);
    1099            0 :                         if (PltSizHeatNum > 0) {
    1100            0 :                             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
    1101            0 :                                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
    1102            0 :                                 int SizingMethod = HVAC::HeatingCapacitySizing;
    1103            0 :                                 int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
    1104            0 :                                 ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
    1105            0 :                                 if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
    1106              :                                     CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
    1107            0 :                                     if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
    1108            0 :                                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == DataSizing::AutoSize) {
    1109            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1110              :                                         } else {
    1111            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1112              :                                         }
    1113            0 :                                         ZoneEqSizing.HeatingCapacity = true;
    1114            0 :                                         TempSize = DataSizing::AutoSize;
    1115            0 :                                     } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
    1116            0 :                                         ZoneEqSizing.HeatingCapacity = true;
    1117            0 :                                         ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
    1118            0 :                                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1119            0 :                                         state.dataSize->DataScalableCapSizingON = true;
    1120            0 :                                     } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
    1121            0 :                                         state.dataSize->DataFracOfAutosizedHeatingCapacity =
    1122            0 :                                             state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1123            0 :                                         TempSize = DataSizing::AutoSize;
    1124            0 :                                         state.dataSize->DataScalableCapSizingON = true;
    1125              :                                     }
    1126              :                                 }
    1127            0 :                                 PrintFlag = false;
    1128            0 :                                 bool errorsFound = false;
    1129            0 :                                 HeatingCapacitySizer sizerHeatingCapacity;
    1130            0 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1131            0 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1132            0 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
    1133            0 :                                 state.dataSize->DataScalableCapSizingON = false;
    1134            0 :                             } else {
    1135            0 :                                 DesCoilLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1136              :                             }
    1137            0 :                             if (DesCoilLoad >= HVAC::SmallLoad) {
    1138            0 :                                 TempSteamIn = 100.00;
    1139            0 :                                 auto *steam = Fluid::GetSteam(state);
    1140            0 :                                 EnthSteamInDry = steam->getSatEnthalpy(state, TempSteamIn, 1.0, RoutineName);
    1141            0 :                                 EnthSteamOutWet = steam->getSatEnthalpy(state, TempSteamIn, 0.0, RoutineName);
    1142            0 :                                 LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
    1143            0 :                                 SteamDensity = steam->getSatDensity(state, TempSteamIn, 1.0, RoutineName);
    1144            0 :                                 MaxVolHotSteamFlowDes =
    1145            0 :                                     DesCoilLoad / (SteamDensity * (LatentHeatSteam +
    1146            0 :                                                                    state.dataSize->PlantSizData(PltSizHeatNum).DeltaT *
    1147            0 :                                                                        Psychrometrics::CPHW(state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp)));
    1148              :                             } else {
    1149            0 :                                 MaxVolHotSteamFlowDes = 0.0;
    1150              :                             }
    1151              :                         } else {
    1152            0 :                             ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
    1153            0 :                             ShowContinueError(state,
    1154            0 :                                               format("Occurs in ZoneHVAC:UnitHeater Object={}", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1155            0 :                             ErrorsFound = true;
    1156              :                         }
    1157            0 :                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
    1158            0 :                         BaseSizer::reportSizerOutput(state,
    1159              :                                                      "ZoneHVAC:UnitHeater",
    1160            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1161              :                                                      "Design Size Maximum Steam Flow [m3/s]",
    1162              :                                                      MaxVolHotSteamFlowDes);
    1163              :                     } else {
    1164            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
    1165            0 :                             MaxVolHotSteamFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
    1166            0 :                             BaseSizer::reportSizerOutput(state,
    1167              :                                                          "ZoneHVAC:UnitHeater",
    1168            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1169              :                                                          "Design Size Maximum Steam Flow [m3/s]",
    1170              :                                                          MaxVolHotSteamFlowDes,
    1171              :                                                          "User-Specified Maximum Steam Flow [m3/s]",
    1172              :                                                          MaxVolHotSteamFlowUser);
    1173            0 :                             if (state.dataGlobal->DisplayExtraWarnings) {
    1174            0 :                                 if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
    1175            0 :                                     state.dataSize->AutoVsHardSizingThreshold) {
    1176            0 :                                     ShowMessage(state,
    1177            0 :                                                 format("SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater {}",
    1178            0 :                                                        state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1179            0 :                                     ShowContinueError(state, format("User-Specified Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
    1180            0 :                                     ShowContinueError(state,
    1181            0 :                                                       format("differs from Design Size Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
    1182            0 :                                     ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1183            0 :                                     ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1184              :                                 }
    1185              :                             }
    1186              :                         }
    1187              :                     }
    1188              :                 }
    1189              :             }
    1190              :         } else {
    1191           63 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = 0.0;
    1192              :         }
    1193              : 
    1194              :         // set the design air flow rate for the heating coil
    1195              : 
    1196           63 :         WaterCoils::SetCoilDesFlow(state,
    1197           63 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
    1198           63 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1199           63 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow,
    1200              :                                    ErrorsFound);
    1201           63 :         if (CurZoneEqNum > 0) {
    1202           63 :             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
    1203           63 :             ZoneEqSizing.MaxHWVolFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
    1204              :         }
    1205              : 
    1206           63 :         if (ErrorsFound) {
    1207            0 :             ShowFatalError(state, "Preceding sizing errors cause program termination");
    1208              :         }
    1209           63 :     }
    1210              : 
    1211       638585 :     void CalcUnitHeater(EnergyPlusData &state,
    1212              :                         int &UnitHeatNum,              // number of the current fan coil unit being simulated
    1213              :                         int const ZoneNum,             // number of zone being served
    1214              :                         bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
    1215              :                         Real64 &PowerMet,              // Sensible power supplied (W)
    1216              :                         Real64 &LatOutputProvided      // Latent power supplied (kg/s), negative = dehumidification
    1217              :     )
    1218              :     {
    1219              : 
    1220              :         // SUBROUTINE INFORMATION:
    1221              :         //       AUTHOR         Rick Strand
    1222              :         //       DATE WRITTEN   May 2000
    1223              :         //       MODIFIED       Don Shirey, Aug 2009 (LatOutputProvided)
    1224              :         //                      July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
    1225              : 
    1226              :         // PURPOSE OF THIS SUBROUTINE:
    1227              :         // This subroutine mainly controls the action of the unit heater
    1228              :         // based on the user input for controls and the defined controls
    1229              :         // algorithms.  There are currently (at the initial creation of this
    1230              :         // subroutine) two control methods: on-off fan operation or continuous
    1231              :         // fan operation.
    1232              : 
    1233              :         // METHODOLOGY EMPLOYED:
    1234              :         // Unit is controlled based on user input and what is happening in the
    1235              :         // simulation.  There are various cases to consider:
    1236              :         // 1. OFF: Unit is schedule off.  All flow rates are set to zero and
    1237              :         //    the temperatures are set to zone conditions.
    1238              :         // 2. NO LOAD OR COOLING/ON-OFF FAN CONTROL: Unit is available, but
    1239              :         //    there is no heating load.  All flow rates are set to zero and
    1240              :         //    the temperatures are set to zone conditions.
    1241              :         // 3. NO LOAD OR COOLING/CONTINUOUS FAN CONTROL: Unit is available and
    1242              :         //    the fan is running (if it is scheduled to be available also).
    1243              :         //    No heating is provided, only circulation via the fan running.
    1244              :         // 4. HEATING: The unit is on/available and there is a heating load.
    1245              :         //    The heating coil is modulated (constant fan speed) to meet the
    1246              :         //    heating load.
    1247              : 
    1248              :         // REFERENCES:
    1249              :         // ASHRAE Systems and Equipment Handbook (SI), 1996. page 31.7
    1250              : 
    1251              :         // SUBROUTINE PARAMETER DEFINITIONS:
    1252       638585 :         int constexpr MaxIter = 100; // maximum number of iterations
    1253              : 
    1254              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1255              :         Real64 SpecHumOut; // Specific humidity ratio of outlet air (kg moisture / kg moist air)
    1256              :         Real64 SpecHumIn;  // Specific humidity ratio of inlet air (kg moisture / kg moist air)
    1257              :         Real64 mdot;       // local temporary for fluid mass flow rate
    1258              : 
    1259              :         // initialize local variables
    1260       638585 :         Real64 QUnitOut = 0.0;
    1261       638585 :         Real64 NoOutput = 0.0;
    1262       638585 :         Real64 FullOutput = 0.0;
    1263       638585 :         Real64 LatentOutput = 0.0; // Latent (moisture) add/removal rate, negative is dehumidification [kg/s]
    1264       638585 :         Real64 MaxWaterFlow = 0.0;
    1265       638585 :         Real64 MinWaterFlow = 0.0;
    1266       638585 :         Real64 PartLoadFrac = 0.0;
    1267       638585 :         int InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
    1268       638585 :         int OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
    1269       638585 :         int ControlNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode;
    1270       638585 :         Real64 ControlOffset = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset;
    1271       638585 :         HVAC::FanOp fanOp = state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp;
    1272              : 
    1273       638585 :         if (fanOp != HVAC::FanOp::Cycling) {
    1274              : 
    1275      1828035 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() <= 0 ||
    1276       605825 :                 ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() <= 0 && !state.dataHVACGlobal->TurnFansOn) ||
    1277       583625 :                  state.dataHVACGlobal->TurnFansOff)) {
    1278              :                 // Case 1: OFF-->unit schedule says that it it not available
    1279              :                 //         OR child fan in not available OR child fan not being cycled ON by sys avail manager
    1280              :                 //         OR child fan being forced OFF by sys avail manager
    1281        54960 :                 state.dataUnitHeaters->HCoilOn = false;
    1282        54960 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1283         4399 :                     mdot = 0.0; // try to turn off
    1284              : 
    1285         8798 :                     PlantUtilities::SetComponentFlowRate(state,
    1286              :                                                          mdot,
    1287         4399 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1288         4399 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1289         4399 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1290              :                 }
    1291        54960 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1292            0 :                     mdot = 0.0; // try to turn off
    1293              : 
    1294            0 :                     PlantUtilities::SetComponentFlowRate(state,
    1295              :                                                          mdot,
    1296            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1297            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1298            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1299              :                 }
    1300        54960 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1301              : 
    1302       583625 :             } else if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
    1303              :                 // Unit is available, but there is no load on it or we are in setback/deadband
    1304       376773 :                 if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating) {
    1305              : 
    1306              :                     // Case 2: NO LOAD OR COOLING/ON-OFF FAN CONTROL-->turn everything off
    1307              :                     //         because there is no load on the unit heater
    1308       125549 :                     state.dataUnitHeaters->HCoilOn = false;
    1309       125549 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1310         9287 :                         mdot = 0.0; // try to turn off
    1311              : 
    1312        18574 :                         PlantUtilities::SetComponentFlowRate(state,
    1313              :                                                              mdot,
    1314         9287 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1315         9287 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1316         9287 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1317              :                     }
    1318       125549 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1319            0 :                         mdot = 0.0; // try to turn off
    1320              : 
    1321            0 :                         PlantUtilities::SetComponentFlowRate(state,
    1322              :                                                              mdot,
    1323            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1324            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1325            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1326              :                     }
    1327       125549 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1328              : 
    1329              :                 } else {
    1330              :                     // Case 3: NO LOAD OR COOLING/CONTINUOUS FAN CONTROL-->let the fan
    1331              :                     //         continue to run even though there is no load (air circulation)
    1332              :                     // Note that the flow rates were already set in the initialization routine
    1333              :                     // so there is really nothing else left to do except call the components.
    1334              : 
    1335       251224 :                     state.dataUnitHeaters->HCoilOn = false;
    1336       251224 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1337         5663 :                         mdot = 0.0; // try to turn off
    1338              : 
    1339         5663 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
    1340        11326 :                             PlantUtilities::SetComponentFlowRate(state,
    1341              :                                                                  mdot,
    1342         5663 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1343         5663 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1344         5663 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1345              :                         }
    1346              :                     }
    1347       251224 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1348            0 :                         mdot = 0.0; // try to turn off
    1349            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
    1350            0 :                             PlantUtilities::SetComponentFlowRate(state,
    1351              :                                                                  mdot,
    1352            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1353            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1354            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1355              :                         }
    1356              :                     }
    1357              : 
    1358       251224 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1359              :                 }
    1360              : 
    1361              :             } else { // Case 4: HEATING-->unit is available and there is a heating load
    1362              : 
    1363       206852 :                 switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1364              : 
    1365        13372 :                 case HCoilType::WaterHeatingCoil: {
    1366              : 
    1367              :                     // On the first HVAC iteration the system values are given to the controller, but after that
    1368              :                     // the demand limits are in place and there needs to be feedback to the Zone Equipment
    1369        13372 :                     if (FirstHVACIteration) {
    1370         6686 :                         MaxWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow;
    1371         6686 :                         MinWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow;
    1372              :                     } else {
    1373         6686 :                         MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
    1374         6686 :                         MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
    1375              :                     }
    1376              :                     // control water flow to obtain output matching QZnReq
    1377        40116 :                     ControlCompOutput(state,
    1378        13372 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1379        13372 :                                       state.dataUnitHeaters->cMO_UnitHeater,
    1380              :                                       UnitHeatNum,
    1381              :                                       FirstHVACIteration,
    1382        13372 :                                       state.dataUnitHeaters->QZnReq,
    1383              :                                       ControlNode,
    1384              :                                       MaxWaterFlow,
    1385              :                                       MinWaterFlow,
    1386              :                                       ControlOffset,
    1387        13372 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).ControlCompTypeNum,
    1388        13372 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).CompErrIndex,
    1389              :                                       _,
    1390              :                                       _,
    1391              :                                       _,
    1392              :                                       _,
    1393              :                                       _,
    1394        13372 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1395        13372 :                     break;
    1396              :                 }
    1397       193480 :                 case HCoilType::Electric:
    1398              :                 case HCoilType::Gas:
    1399              :                 case HCoilType::SteamCoil: {
    1400       193480 :                     state.dataUnitHeaters->HCoilOn = true;
    1401       193480 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1402       193480 :                     break;
    1403              :                 }
    1404            0 :                 default:
    1405            0 :                     break;
    1406              :                 }
    1407              :             }
    1408       638585 :             if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
    1409       560996 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
    1410       560996 :                     state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
    1411              :             }
    1412              :         } else { // OnOff fan and cycling
    1413            0 :             if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) ||
    1414            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() <= 0 ||
    1415            0 :                 ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() <= 0 && !state.dataHVACGlobal->TurnFansOn) ||
    1416            0 :                  state.dataHVACGlobal->TurnFansOff)) {
    1417              :                 // Case 1: OFF-->unit schedule says that it it not available
    1418              :                 //         OR child fan in not available OR child fan not being cycled ON by sys avail manager
    1419              :                 //         OR child fan being forced OFF by sys avail manager
    1420            0 :                 PartLoadFrac = 0.0;
    1421            0 :                 state.dataUnitHeaters->HCoilOn = false;
    1422            0 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
    1423              : 
    1424            0 :                 if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
    1425            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
    1426            0 :                         state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
    1427              :                 }
    1428              : 
    1429              :             } else { // Case 4: HEATING-->unit is available and there is a heating load
    1430              : 
    1431            0 :                 state.dataUnitHeaters->HCoilOn = true;
    1432              : 
    1433              :                 // Find part load ratio of unit heater coils
    1434            0 :                 PartLoadFrac = 0.0;
    1435            0 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, NoOutput, fanOp, PartLoadFrac);
    1436            0 :                 if ((NoOutput - state.dataUnitHeaters->QZnReq) < HVAC::SmallLoad) {
    1437              :                     // Unit heater is unable to meet the load with coil off, set PLR = 1
    1438            0 :                     PartLoadFrac = 1.0;
    1439            0 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, FullOutput, fanOp, PartLoadFrac);
    1440            0 :                     if ((FullOutput - state.dataUnitHeaters->QZnReq) > HVAC::SmallLoad) {
    1441              :                         // Unit heater full load capacity is able to meet the load, Find PLR
    1442              : 
    1443            0 :                         auto f = [&state, UnitHeatNum, FirstHVACIteration, fanOp](Real64 const PartLoadRatio) {
    1444              :                             // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1445              :                             Real64 QUnitOut; // heating provided by unit heater [watts]
    1446              : 
    1447            0 :                             CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadRatio);
    1448              : 
    1449              :                             // Calculate residual based on output calculation flag
    1450            0 :                             if (state.dataUnitHeaters->QZnReq != 0.0) {
    1451            0 :                                 return (QUnitOut - state.dataUnitHeaters->QZnReq) / state.dataUnitHeaters->QZnReq;
    1452              :                             } else {
    1453            0 :                                 return 0.0;
    1454              :                             }
    1455            0 :                         };
    1456              : 
    1457              :                         // Tolerance is in fraction of load, MaxIter = 30, SolFalg = # of iterations or error as appropriate
    1458            0 :                         int SolFlag = 0; // # of iterations IF positive, -1 means failed to converge, -2 means bounds are incorrect
    1459            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadFrac, f, 0.0, 1.0);
    1460              :                     }
    1461              :                 }
    1462              : 
    1463            0 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
    1464              : 
    1465              :             } // ...end of unit ON/OFF IF-THEN block
    1466            0 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).PartLoadFrac = PartLoadFrac;
    1467            0 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio = PartLoadFrac;
    1468            0 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    1469              :         }
    1470              : 
    1471              :         // CR9155 Remove specific humidity calculations
    1472       638585 :         SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
    1473       638585 :         SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
    1474       638585 :         LatentOutput = state.dataLoopNodes->Node(OutletNode).MassFlowRate * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
    1475              : 
    1476       638585 :         QUnitOut = state.dataLoopNodes->Node(OutletNode).MassFlowRate *
    1477       638585 :                    (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
    1478       638585 :                     Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
    1479              : 
    1480              :         // Report variables...
    1481       638585 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower = max(0.0, QUnitOut);
    1482       638585 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower =
    1483       638585 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->totalPower;
    1484              : 
    1485       638585 :         PowerMet = QUnitOut;
    1486       638585 :         LatOutputProvided = LatentOutput;
    1487       638585 :     }
    1488              : 
    1489       787988 :     void CalcUnitHeaterComponents(EnergyPlusData &state,
    1490              :                                   int const UnitHeatNum,         // Unit index in unit heater array
    1491              :                                   bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
    1492              :                                   Real64 &LoadMet,               // load met by unit (watts)
    1493              :                                   HVAC::FanOp const fanOp,       // fan operating mode
    1494              :                                   Real64 const PartLoadRatio     // part-load ratio
    1495              :     )
    1496              :     {
    1497              : 
    1498              :         // SUBROUTINE INFORMATION:
    1499              :         //       AUTHOR         Rick Strand
    1500              :         //       DATE WRITTEN   May 2000
    1501              :         //       MODIFIED       July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
    1502              : 
    1503              :         // PURPOSE OF THIS SUBROUTINE:
    1504              :         // This subroutine launches the individual component simulations.
    1505              :         // This is called either when the unit is off to carry null conditions
    1506              :         // through the unit or during control iterations to continue updating
    1507              :         // what is going on within the unit.
    1508              : 
    1509              :         // METHODOLOGY EMPLOYED:
    1510              :         // Simply calls the different components in order.
    1511              : 
    1512              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1513              :         Real64 AirMassFlow; // total mass flow through the unit
    1514              :         Real64 CpAirZn;     // specific heat of dry air at zone conditions (zone conditions same as unit inlet)
    1515              :         int HCoilInAirNode; // inlet node number for fan exit/coil inlet
    1516              :         Real64 mdot;        // local temporary for fluid mass flow rate
    1517              : 
    1518       787988 :         int InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
    1519       787988 :         int OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
    1520       787988 :         Real64 QCoilReq = 0.0;
    1521              : 
    1522       787988 :         if (fanOp != HVAC::FanOp::Cycling) {
    1523       787988 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1524              : 
    1525       787988 :             switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1526              : 
    1527       182124 :             case HCoilType::WaterHeatingCoil: {
    1528              : 
    1529       364248 :                 WaterCoils::SimulateWaterCoilComponents(state,
    1530       182124 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1531              :                                                         FirstHVACIteration,
    1532       182124 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
    1533       182124 :                 break;
    1534              :             }
    1535            0 :             case HCoilType::SteamCoil: {
    1536              : 
    1537            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1538            0 :                     QCoilReq = 0.0;
    1539              :                 } else {
    1540            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1541            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1542            0 :                     QCoilReq =
    1543            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1544            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1545            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1546              :                 }
    1547            0 :                 if (QCoilReq < 0.0) {
    1548            0 :                     QCoilReq = 0.0; // a heating coil can only heat, not cool
    1549              :                 }
    1550            0 :                 SteamCoils::SimulateSteamCoilComponents(state,
    1551            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1552              :                                                         FirstHVACIteration,
    1553            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1554              :                                                         QCoilReq);
    1555            0 :                 break;
    1556              :             }
    1557       605864 :             case HCoilType::Electric:
    1558              :             case HCoilType::Gas: {
    1559              : 
    1560       605864 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1561       412384 :                     QCoilReq = 0.0;
    1562              :                 } else {
    1563       193480 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1564       193480 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1565       193480 :                     QCoilReq =
    1566       193480 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1567       193480 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1568       193480 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1569              :                 }
    1570       605864 :                 if (QCoilReq < 0.0) {
    1571          792 :                     QCoilReq = 0.0; // a heating coil can only heat, not cool
    1572              :                 }
    1573      1817592 :                 HeatingCoils::SimulateHeatingCoilComponents(state,
    1574       605864 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1575              :                                                             FirstHVACIteration,
    1576              :                                                             QCoilReq,
    1577       605864 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
    1578       605864 :                 break;
    1579              :             }
    1580            0 :             default:
    1581            0 :                 break;
    1582              :             }
    1583              : 
    1584       787988 :             AirMassFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
    1585              : 
    1586       787988 :             state.dataLoopNodes->Node(InletNode).MassFlowRate =
    1587       787988 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate; // maintain continuity through unit heater
    1588              : 
    1589              :         } else { // OnOff fan cycling
    1590              : 
    1591            0 :             state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateMax * PartLoadRatio;
    1592            0 :             AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    1593              :             // Set the fan inlet node maximum available mass flow rates for cycling fans
    1594            0 :             state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
    1595              : 
    1596            0 :             if (QCoilReq < 0.0) {
    1597            0 :                 QCoilReq = 0.0; // a heating coil can only heat, not cool
    1598              :             }
    1599            0 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1600              : 
    1601            0 :             switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1602              : 
    1603            0 :             case HCoilType::WaterHeatingCoil: {
    1604              : 
    1605            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1606            0 :                     mdot = 0.0;
    1607            0 :                     QCoilReq = 0.0;
    1608              :                 } else {
    1609            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1610            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1611            0 :                     QCoilReq =
    1612            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1613            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1614            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1615            0 :                     mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow * PartLoadRatio;
    1616              :                 }
    1617            0 :                 if (QCoilReq < 0.0) {
    1618            0 :                     QCoilReq = 0.0; // a heating coil can only heat, not cool
    1619              :                 }
    1620            0 :                 PlantUtilities::SetComponentFlowRate(state,
    1621              :                                                      mdot,
    1622            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1623            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1624            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1625            0 :                 WaterCoils::SimulateWaterCoilComponents(state,
    1626            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1627              :                                                         FirstHVACIteration,
    1628            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1629              :                                                         QCoilReq,
    1630              :                                                         fanOp,
    1631              :                                                         PartLoadRatio);
    1632            0 :                 break;
    1633              :             }
    1634            0 :             case HCoilType::SteamCoil: {
    1635            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1636            0 :                     mdot = 0.0;
    1637            0 :                     QCoilReq = 0.0;
    1638              :                 } else {
    1639            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1640            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1641            0 :                     QCoilReq =
    1642            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1643            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1644            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1645            0 :                     mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow * PartLoadRatio;
    1646              :                 }
    1647            0 :                 if (QCoilReq < 0.0) {
    1648            0 :                     QCoilReq = 0.0; // a heating coil can only heat, not cool
    1649              :                 }
    1650            0 :                 PlantUtilities::SetComponentFlowRate(state,
    1651              :                                                      mdot,
    1652            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1653            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1654            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1655            0 :                 SteamCoils::SimulateSteamCoilComponents(state,
    1656            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1657              :                                                         FirstHVACIteration,
    1658            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1659              :                                                         QCoilReq,
    1660              :                                                         _,
    1661              :                                                         fanOp,
    1662              :                                                         PartLoadRatio);
    1663            0 :                 break;
    1664              :             }
    1665            0 :             case HCoilType::Electric:
    1666              :             case HCoilType::Gas: {
    1667              : 
    1668            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1669            0 :                     QCoilReq = 0.0;
    1670              :                 } else {
    1671            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1672            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1673            0 :                     QCoilReq =
    1674            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1675            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1676            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1677              :                 }
    1678            0 :                 if (QCoilReq < 0.0) {
    1679            0 :                     QCoilReq = 0.0; // a heating coil can only heat, not cool
    1680              :                 }
    1681            0 :                 HeatingCoils::SimulateHeatingCoilComponents(state,
    1682            0 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1683              :                                                             FirstHVACIteration,
    1684              :                                                             QCoilReq,
    1685            0 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1686              :                                                             _,
    1687              :                                                             _,
    1688              :                                                             fanOp,
    1689              :                                                             PartLoadRatio);
    1690            0 :                 break;
    1691              :             }
    1692            0 :             default:
    1693            0 :                 break;
    1694              :             }
    1695            0 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate =
    1696            0 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate; // maintain continuity through unit heater
    1697              :         }
    1698       787988 :         LoadMet = AirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
    1699       787988 :                                  Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
    1700       787988 :     }
    1701              : 
    1702              :     // SUBROUTINE UpdateUnitHeater
    1703              : 
    1704              :     // No update routine needed in this module since all of the updates happen on
    1705              :     // the Node derived type directly and these updates are done by other routines.
    1706              : 
    1707              :     // END SUBROUTINE UpdateUnitHeater
    1708              : 
    1709       638585 :     void ReportUnitHeater(EnergyPlusData &state, int const UnitHeatNum) // Unit index in unit heater array
    1710              :     {
    1711              : 
    1712              :         // SUBROUTINE INFORMATION:
    1713              :         //       AUTHOR         Rick Strand
    1714              :         //       DATE WRITTEN   May 2000
    1715              : 
    1716              :         // PURPOSE OF THIS SUBROUTINE:
    1717              :         // This subroutine needs a description.
    1718              : 
    1719              :         // METHODOLOGY EMPLOYED:
    1720              :         // Needs description, as appropriate.
    1721              : 
    1722              :         // Using/Aliasing
    1723       638585 :         Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    1724              : 
    1725       638585 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatEnergy = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower * TimeStepSysSec;
    1726       638585 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecEnergy = state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower * TimeStepSysSec;
    1727              : 
    1728       638585 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass) { // reset sizing flags so other zone equipment can size normally
    1729          116 :             if (!state.dataGlobal->SysSizingCalc) {
    1730           63 :                 DataSizing::resetHVACSizingGlobals(state, state.dataSize->CurZoneEqNum, 0, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass);
    1731              :             }
    1732              :         }
    1733       638585 :     }
    1734              : 
    1735            0 :     int getUnitHeaterIndex(EnergyPlusData &state, std::string_view CompName)
    1736              :     {
    1737            0 :         if (state.dataUnitHeaters->GetUnitHeaterInputFlag) {
    1738            0 :             GetUnitHeaterInput(state);
    1739            0 :             state.dataUnitHeaters->GetUnitHeaterInputFlag = false;
    1740              :         }
    1741              : 
    1742            0 :         for (int UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
    1743            0 :             if (Util::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name, CompName)) {
    1744            0 :                 return UnitHeatNum;
    1745              :             }
    1746              :         }
    1747              : 
    1748            0 :         return 0;
    1749              :     }
    1750              : 
    1751              : } // namespace UnitHeater
    1752              : 
    1753              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1