LCOV - code coverage report
Current view: top level - EnergyPlus - UnitHeater.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 53.6 % 894 479
Test Date: 2025-05-22 16:09:37 Functions: 88.9 % 9 8

            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          362 :     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          362 :         if (state.dataUnitHeaters->GetUnitHeaterInputFlag) {
     133            3 :             GetUnitHeaterInput(state);
     134            3 :             state.dataUnitHeaters->GetUnitHeaterInputFlag = false;
     135              :         }
     136              : 
     137              :         // Find the correct Unit Heater Equipment
     138          362 :         if (CompIndex == 0) {
     139            2 :             UnitHeatNum = Util::FindItemInList(CompName, state.dataUnitHeaters->UnitHeat);
     140            2 :             if (UnitHeatNum == 0) {
     141            0 :                 ShowFatalError(state, format("SimUnitHeater: Unit not found={}", CompName));
     142              :             }
     143            2 :             CompIndex = UnitHeatNum;
     144              :         } else {
     145          360 :             UnitHeatNum = CompIndex;
     146          360 :             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          360 :             if (state.dataUnitHeaters->CheckEquipName(UnitHeatNum)) {
     154            2 :                 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            2 :                 state.dataUnitHeaters->CheckEquipName(UnitHeatNum) = false;
     162              :             }
     163              :         }
     164              : 
     165          362 :         state.dataSize->ZoneEqUnitHeater = true;
     166              : 
     167          362 :         InitUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration);
     168              : 
     169          362 :         state.dataSize->ZoneHeatingOnlyFan = true;
     170              : 
     171          362 :         CalcUnitHeater(state, UnitHeatNum, ZoneNum, FirstHVACIteration, PowerMet, LatOutputProvided);
     172              : 
     173          362 :         state.dataSize->ZoneHeatingOnlyFan = false;
     174              : 
     175              :         //  CALL UpdateUnitHeater
     176              : 
     177          362 :         ReportUnitHeater(state, UnitHeatNum);
     178              : 
     179          362 :         state.dataSize->ZoneEqUnitHeater = false;
     180          362 :     }
     181              : 
     182            4 :     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            4 :         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            4 :         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            4 :         Array1D_string Alphas;         // Alpha items for object
     215            4 :         Array1D<Real64> Numbers;       // Numeric items for object
     216            4 :         Array1D_string cAlphaFields;   // Alpha field names
     217            4 :         Array1D_string cNumericFields; // Numeric field names
     218            4 :         Array1D_bool lAlphaBlanks;     // Logical array, alpha field input BLANK = .TRUE.
     219            4 :         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            4 :         std::string CurrentModuleObject = state.dataUnitHeaters->cMO_UnitHeater;
     225            4 :         state.dataUnitHeaters->NumOfUnitHeats = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     226            4 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
     227              : 
     228            4 :         Alphas.allocate(NumAlphas);
     229            4 :         Numbers.dimension(NumNumbers, 0.0);
     230            4 :         cAlphaFields.allocate(NumAlphas);
     231            4 :         cNumericFields.allocate(NumNumbers);
     232            4 :         lAlphaBlanks.dimension(NumAlphas, true);
     233            4 :         lNumericBlanks.dimension(NumNumbers, true);
     234              : 
     235              :         // Allocate the local derived type and do one-time initializations for all parts of it
     236            4 :         if (state.dataUnitHeaters->NumOfUnitHeats > 0) {
     237            4 :             state.dataUnitHeaters->UnitHeat.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     238            4 :             state.dataUnitHeaters->CheckEquipName.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     239            4 :             state.dataUnitHeaters->UnitHeatNumericFields.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     240              :         }
     241            4 :         state.dataUnitHeaters->CheckEquipName = true;
     242              : 
     243            8 :         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            4 :             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            4 :             ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
     260              : 
     261            4 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames.allocate(NumNumbers);
     262            4 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = "";
     263            4 :             state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames = cNumericFields;
     264            4 :             Util::IsNameEmpty(state, Alphas(1), CurrentModuleObject, ErrorsFound);
     265              : 
     266            4 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name = Alphas(1);
     267              : 
     268            4 :             if (lAlphaBlanks(2)) {
     269            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched = Sched::GetScheduleAlwaysOn(state);
     270            4 :             } 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            4 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode =
     277            8 :                 NodeInputManager::GetOnlySingleNode(state,
     278            4 :                                                     Alphas(3),
     279              :                                                     ErrorsFound,
     280              :                                                     DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
     281            4 :                                                     Alphas(1),
     282              :                                                     DataLoopNode::NodeFluidType::Air,
     283              :                                                     DataLoopNode::ConnectionType::Inlet,
     284              :                                                     NodeInputManager::CompFluidStream::Primary,
     285              :                                                     DataLoopNode::ObjectIsParent);
     286              : 
     287            4 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode =
     288            8 :                 NodeInputManager::GetOnlySingleNode(state,
     289            4 :                                                     Alphas(4),
     290              :                                                     ErrorsFound,
     291              :                                                     DataLoopNode::ConnectionObjectType::ZoneHVACUnitHeater,
     292            4 :                                                     Alphas(1),
     293              :                                                     DataLoopNode::NodeFluidType::Air,
     294              :                                                     DataLoopNode::ConnectionType::Outlet,
     295              :                                                     NodeInputManager::CompFluidStream::Primary,
     296              :                                                     DataLoopNode::ObjectIsParent);
     297              : 
     298            4 :             auto &unitHeat = state.dataUnitHeaters->UnitHeat(UnitHeatNum);
     299              :             // Fan information:
     300            4 :             unitHeat.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, Alphas(5)));
     301            4 :             if (unitHeat.fanType != HVAC::FanType::Constant && unitHeat.fanType != HVAC::FanType::VAV && unitHeat.fanType != HVAC::FanType::OnOff &&
     302            0 :                 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            4 :             unitHeat.FanName = Alphas(6);
     308            4 :             unitHeat.MaxAirVolFlow = Numbers(1);
     309              : 
     310            4 :             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            4 :                 auto *fan = state.dataFans->fans(unitHeat.Fan_Index);
     316              : 
     317            4 :                 unitHeat.FanOutletNode = fan->outletNodeNum;
     318              : 
     319            4 :                 FanVolFlow = fan->maxAirFlowRate;
     320              : 
     321            4 :                 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            4 :                 } 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            4 :                 } 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            4 :                 unitHeat.fanAvailSched = fan->availSched;
     341              :             }
     342              : 
     343              :             // Heating coil information:
     344              :             {
     345            4 :                 unitHeat.Type = static_cast<HCoilType>(getEnumValue(HCoilTypeNamesUC, Util::makeUPPER(Alphas(7))));
     346            4 :                 switch (unitHeat.Type) {
     347            3 :                 case HCoilType::WaterHeatingCoil:
     348            3 :                     unitHeat.HeatingCoilType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
     349            3 :                     break;
     350            0 :                 case HCoilType::SteamCoil:
     351            0 :                     unitHeat.HeatingCoilType = DataPlant::PlantEquipmentType::CoilSteamAirHeating;
     352            0 :                     break;
     353            1 :                 case HCoilType::Electric:
     354              :                 case HCoilType::Gas:
     355            1 :                     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            4 :             if (!errFlag) {
     366            4 :                 unitHeat.HCoilTypeCh = Alphas(7);
     367            4 :                 unitHeat.HCoilName = Alphas(8);
     368            4 :                 ValidateComponent(state, Alphas(7), unitHeat.HCoilName, IsNotOK, CurrentModuleObject);
     369            4 :                 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            4 :                     if (unitHeat.Type == HCoilType::WaterHeatingCoil || unitHeat.Type == HCoilType::SteamCoil) {
     376              :                         // mine the hot water or steam node from the coil object
     377            3 :                         errFlag = false;
     378            3 :                         if (unitHeat.Type == HCoilType::WaterHeatingCoil) {
     379            3 :                             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            3 :                         if (errFlag) {
     387            0 :                             ShowContinueError(state, format("that was specified in {} = \"{}\"", CurrentModuleObject, unitHeat.Name));
     388            0 :                             ErrorsFound = true;
     389              :                         }
     390              :                     }
     391              :                 }
     392              :             }
     393              : 
     394            4 :             if (lAlphaBlanks(9)) {
     395           11 :                 unitHeat.fanOp = (unitHeat.fanType == HVAC::FanType::OnOff || unitHeat.fanType == HVAC::FanType::SystemModel)
     396            4 :                                      ? HVAC::FanOp::Cycling
     397              :                                      : HVAC::FanOp::Continuous;
     398            0 :             } 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            0 :             } 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            4 :             unitHeat.FanOperatesDuringNoHeating = Alphas(10);
     408            4 :             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            4 :             } else if (Util::SameString(unitHeat.FanOperatesDuringNoHeating, "No")) {
     413            4 :                 unitHeat.FanOffNoHeating = true;
     414              :             }
     415              : 
     416            4 :             unitHeat.MaxVolHotWaterFlow = Numbers(2);
     417            4 :             unitHeat.MinVolHotWaterFlow = Numbers(3);
     418            4 :             unitHeat.MaxVolHotSteamFlow = Numbers(2);
     419            4 :             unitHeat.MinVolHotSteamFlow = Numbers(3);
     420              : 
     421            4 :             unitHeat.HotControlOffset = Numbers(4);
     422              :             // Set default convergence tolerance
     423            4 :             if (unitHeat.HotControlOffset <= 0.0) {
     424            0 :                 unitHeat.HotControlOffset = 0.001;
     425              :             }
     426              : 
     427            4 :             if (!lAlphaBlanks(11)) {
     428            0 :                 unitHeat.AvailManagerListName = Alphas(11);
     429              :             }
     430              : 
     431            4 :             unitHeat.HVACSizingIndex = 0;
     432            4 :             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            4 :             bool ZoneNodeNotFound = true;
     443            8 :             for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     444            4 :                 if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
     445            4 :                 for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumExhaustNodes; ++NodeNum) {
     446            4 :                     if (unitHeat.AirInNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).ExhaustNode(NodeNum)) {
     447            4 :                         ZoneNodeNotFound = false;
     448            4 :                         break;
     449              :                     }
     450              :                 }
     451              :             }
     452            4 :             if (ZoneNodeNotFound) {
     453            0 :                 ShowSevereError(state,
     454            0 :                                 format("{} = \"{}\". Unit heater air inlet node name must be the same as a zone exhaust node name.",
     455              :                                        CurrentModuleObject,
     456            0 :                                        unitHeat.Name));
     457            0 :                 ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
     458            0 :                 ShowContinueError(state, format("..Unit heater air inlet node name = {}", state.dataLoopNodes->NodeID(unitHeat.AirInNode)));
     459            0 :                 ErrorsFound = true;
     460              :             }
     461              :             // check that unit heater air outlet node is a zone inlet node.
     462            4 :             ZoneNodeNotFound = true;
     463            8 :             for (CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     464            4 :                 if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) continue;
     465            5 :                 for (NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++NodeNum) {
     466            5 :                     if (unitHeat.AirOutNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(NodeNum)) {
     467            4 :                         unitHeat.ZonePtr = CtrlZone;
     468            4 :                         ZoneNodeNotFound = false;
     469            4 :                         break;
     470              :                     }
     471              :                 }
     472              :             }
     473            4 :             if (ZoneNodeNotFound) {
     474            0 :                 ShowSevereError(state,
     475            0 :                                 format("{} = \"{}\". Unit heater air outlet node name must be the same as a zone inlet node name.",
     476              :                                        CurrentModuleObject,
     477            0 :                                        unitHeat.Name));
     478            0 :                 ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
     479            0 :                 ShowContinueError(state, format("..Unit heater air outlet node name = {}", state.dataLoopNodes->NodeID(unitHeat.AirOutNode)));
     480            0 :                 ErrorsFound = true;
     481              :             }
     482              : 
     483              :             // Add fan to component sets array
     484           12 :             BranchNodeConnections::SetUpCompSets(state,
     485              :                                                  CurrentModuleObject,
     486              :                                                  unitHeat.Name,
     487            4 :                                                  HVAC::fanTypeNamesUC[(int)unitHeat.fanType],
     488              :                                                  unitHeat.FanName,
     489            4 :                                                  state.dataLoopNodes->NodeID(unitHeat.AirInNode),
     490            4 :                                                  state.dataLoopNodes->NodeID(unitHeat.FanOutletNode));
     491              : 
     492              :             // Add heating coil to component sets array
     493            8 :             BranchNodeConnections::SetUpCompSets(state,
     494              :                                                  CurrentModuleObject,
     495              :                                                  unitHeat.Name,
     496              :                                                  unitHeat.HCoilTypeCh,
     497              :                                                  unitHeat.HCoilName,
     498            4 :                                                  state.dataLoopNodes->NodeID(unitHeat.FanOutletNode),
     499            4 :                                                  state.dataLoopNodes->NodeID(unitHeat.AirOutNode));
     500              : 
     501              :         } // ...loop over all of the unit heaters found in the input file
     502              : 
     503            4 :         Alphas.deallocate();
     504            4 :         Numbers.deallocate();
     505            4 :         cAlphaFields.deallocate();
     506            4 :         cNumericFields.deallocate();
     507            4 :         lAlphaBlanks.deallocate();
     508            4 :         lNumericBlanks.deallocate();
     509              : 
     510            4 :         if (ErrorsFound) ShowFatalError(state, format("{}Errors found in input", RoutineName));
     511              : 
     512              :         // Setup Report variables for the Unit Heaters, CurrentModuleObject='ZoneHVAC:UnitHeater'
     513            8 :         for (int UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
     514            4 :             auto &unitHeat = state.dataUnitHeaters->UnitHeat(UnitHeatNum);
     515            8 :             SetupOutputVariable(state,
     516              :                                 "Zone Unit Heater Heating Rate",
     517              :                                 Constant::Units::W,
     518            4 :                                 unitHeat.HeatPower,
     519              :                                 OutputProcessor::TimeStepType::System,
     520              :                                 OutputProcessor::StoreType::Average,
     521            4 :                                 unitHeat.Name);
     522            8 :             SetupOutputVariable(state,
     523              :                                 "Zone Unit Heater Heating Energy",
     524              :                                 Constant::Units::J,
     525            4 :                                 unitHeat.HeatEnergy,
     526              :                                 OutputProcessor::TimeStepType::System,
     527              :                                 OutputProcessor::StoreType::Sum,
     528            4 :                                 unitHeat.Name);
     529            8 :             SetupOutputVariable(state,
     530              :                                 "Zone Unit Heater Fan Electricity Rate",
     531              :                                 Constant::Units::W,
     532            4 :                                 unitHeat.ElecPower,
     533              :                                 OutputProcessor::TimeStepType::System,
     534              :                                 OutputProcessor::StoreType::Average,
     535            4 :                                 unitHeat.Name);
     536              :             // Note that the unit heater fan electric is NOT metered because this value is already metered through the fan component
     537            8 :             SetupOutputVariable(state,
     538              :                                 "Zone Unit Heater Fan Electricity Energy",
     539              :                                 Constant::Units::J,
     540            4 :                                 unitHeat.ElecEnergy,
     541              :                                 OutputProcessor::TimeStepType::System,
     542              :                                 OutputProcessor::StoreType::Sum,
     543            4 :                                 unitHeat.Name);
     544            4 :             SetupOutputVariable(state,
     545              :                                 "Zone Unit Heater Fan Availability Status",
     546              :                                 Constant::Units::None,
     547            4 :                                 (int &)unitHeat.availStatus,
     548              :                                 OutputProcessor::TimeStepType::System,
     549              :                                 OutputProcessor::StoreType::Average,
     550            4 :                                 unitHeat.Name);
     551            4 :             if (unitHeat.fanType == HVAC::FanType::OnOff) {
     552            2 :                 SetupOutputVariable(state,
     553              :                                     "Zone Unit Heater Fan Part Load Ratio",
     554              :                                     Constant::Units::None,
     555            1 :                                     unitHeat.FanPartLoadRatio,
     556              :                                     OutputProcessor::TimeStepType::System,
     557              :                                     OutputProcessor::StoreType::Average,
     558            1 :                                     unitHeat.Name);
     559              :             }
     560            4 :             state.dataRptCoilSelection->coilSelectionReportObj->setCoilSupplyFanInfo(
     561            4 :                 state, unitHeat.HCoilName, unitHeat.HCoilTypeCh, unitHeat.FanName, unitHeat.fanType, unitHeat.Fan_Index);
     562              :         }
     563            4 :     }
     564              : 
     565          363 :     void InitUnitHeater(EnergyPlusData &state,
     566              :                         int const UnitHeatNum,                         // index for the current unit heater
     567              :                         int const ZoneNum,                             // number of zone being served
     568              :                         [[maybe_unused]] bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
     569              :     )
     570              :     {
     571              : 
     572              :         // SUBROUTINE INFORMATION:
     573              :         //       AUTHOR         Rick Strand
     574              :         //       DATE WRITTEN   May 2000
     575              :         //       MODIFIED       Chandan Sharma, FSEC, March 2011: Added ZoneHVAC sys avail manager
     576              : 
     577              :         // PURPOSE OF THIS SUBROUTINE:
     578              :         // This subroutine initializes all of the data elements which are necessary
     579              :         // to simulate a unit heater.
     580              : 
     581              :         // METHODOLOGY EMPLOYED:
     582              :         // Uses the status flags to trigger initializations.
     583              : 
     584              :         // SUBROUTINE PARAMETER DEFINITIONS:
     585              :         static constexpr std::string_view RoutineName("InitUnitHeater");
     586              : 
     587              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     588              :         int InNode;    // inlet node number in unit heater loop
     589              :         int OutNode;   // outlet node number in unit heater loop
     590              :         Real64 RhoAir; // air density at InNode
     591              :         Real64 TempSteamIn;
     592              :         Real64 SteamDensity;
     593              :         Real64 rho; // local fluid density
     594              : 
     595              :         // Do the one time initializations
     596          363 :         if (state.dataUnitHeaters->InitUnitHeaterOneTimeFlag) {
     597              : 
     598            3 :             state.dataUnitHeaters->MyEnvrnFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     599            3 :             state.dataUnitHeaters->MySizeFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     600            3 :             state.dataUnitHeaters->MyPlantScanFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     601            3 :             state.dataUnitHeaters->MyZoneEqFlag.allocate(state.dataUnitHeaters->NumOfUnitHeats);
     602            3 :             state.dataUnitHeaters->MyEnvrnFlag = true;
     603            3 :             state.dataUnitHeaters->MySizeFlag = true;
     604            3 :             state.dataUnitHeaters->MyPlantScanFlag = true;
     605            3 :             state.dataUnitHeaters->MyZoneEqFlag = true;
     606            3 :             state.dataUnitHeaters->InitUnitHeaterOneTimeFlag = false;
     607              :         }
     608              : 
     609          363 :         if (allocated(state.dataAvail->ZoneComp)) {
     610          361 :             auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::UnitHeater).ZoneCompAvailMgrs(UnitHeatNum);
     611          361 :             if (state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum)) { // initialize the name of each availability manager list and zone number
     612            2 :                 availMgr.AvailManagerListName = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AvailManagerListName;
     613            2 :                 availMgr.ZoneNum = ZoneNum;
     614            2 :                 state.dataUnitHeaters->MyZoneEqFlag(UnitHeatNum) = false;
     615              :             }
     616          361 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).availStatus = availMgr.availStatus;
     617              :         }
     618              : 
     619          363 :         if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && allocated(state.dataPlnt->PlantLoop)) {
     620            4 :             if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
     621            1 :                 (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
     622            2 :                 bool errFlag = false;
     623            4 :                 PlantUtilities::ScanPlantLoopsForObject(state,
     624            2 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
     625            2 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatingCoilType,
     626            2 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc,
     627              :                                                         errFlag,
     628              :                                                         _,
     629              :                                                         _,
     630              :                                                         _,
     631              :                                                         _,
     632              :                                                         _);
     633            2 :                 if (errFlag) {
     634            0 :                     ShowContinueError(state,
     635            0 :                                       format("Reference Unit=\"{}\", type=ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
     636            0 :                     ShowFatalError(state, "InitUnitHeater: Program terminated due to previous condition(s).");
     637              :                 }
     638              : 
     639            2 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum =
     640            2 :                     DataPlant::CompData::getPlantComponent(state, state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc).NodeNumOut;
     641              :             }
     642            3 :             state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
     643          360 :         } else if (state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) && !state.dataGlobal->AnyPlantInModel) {
     644            0 :             state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum) = false;
     645              :         }
     646              :         // need to check all units to see if they are on Zone Equipment List or issue warning
     647          363 :         if (!state.dataUnitHeaters->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
     648            2 :             state.dataUnitHeaters->ZoneEquipmentListChecked = true;
     649            4 :             for (int Loop = 1; Loop <= state.dataUnitHeaters->NumOfUnitHeats; ++Loop) {
     650            2 :                 if (DataZoneEquipment::CheckZoneEquipmentList(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(Loop).Name)) continue;
     651            0 :                 ShowSevereError(state,
     652            0 :                                 format("InitUnitHeater: Unit=[UNIT HEATER,{}] is not on any ZoneHVAC:EquipmentList.  It will not be simulated.",
     653            0 :                                        state.dataUnitHeaters->UnitHeat(Loop).Name));
     654              :             }
     655              :         }
     656              : 
     657          365 :         if (!state.dataGlobal->SysSizingCalc && state.dataUnitHeaters->MySizeFlag(UnitHeatNum) &&
     658            2 :             !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
     659              : 
     660            2 :             SizeUnitHeater(state, UnitHeatNum);
     661              : 
     662            2 :             state.dataUnitHeaters->MySizeFlag(UnitHeatNum) = false;
     663              :         } // Do the one time initializations
     664              : 
     665          366 :         if (state.dataGlobal->BeginEnvrnFlag && state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) &&
     666            3 :             !state.dataUnitHeaters->MyPlantScanFlag(UnitHeatNum)) {
     667            3 :             InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
     668            3 :             OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
     669            3 :             RhoAir = state.dataEnvrn->StdRhoAir;
     670              : 
     671              :             // set the mass flow rates from the input volume flow rates
     672            3 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow = RhoAir * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
     673              : 
     674              :             // set the node max and min mass flow rates
     675            3 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     676            3 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
     677              : 
     678            3 :             state.dataLoopNodes->Node(InNode).MassFlowRateMax = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     679            3 :             state.dataLoopNodes->Node(InNode).MassFlowRateMin = 0.0;
     680              : 
     681            3 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
     682            1 :                 rho = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
     683            1 :                           .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     684              : 
     685            1 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
     686            1 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow = rho * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotWaterFlow;
     687            4 :                 PlantUtilities::InitComponentNodes(state,
     688            1 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow,
     689            1 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow,
     690            1 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
     691            1 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
     692              :             }
     693            3 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
     694            0 :                 TempSteamIn = 100.00;
     695            0 :                 SteamDensity = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_fluid->getSatDensity(state, TempSteamIn, 1.0, RoutineName);
     696            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow =
     697            0 :                     SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
     698            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow =
     699            0 :                     SteamDensity * state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinVolHotSteamFlow;
     700              : 
     701            0 :                 PlantUtilities::InitComponentNodes(state,
     702            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotSteamFlow,
     703            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow,
     704            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
     705            0 :                                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum);
     706              :             }
     707              : 
     708            3 :             state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = false;
     709              :         } // ...end start of environment inits
     710              : 
     711          363 :         if (!state.dataGlobal->BeginEnvrnFlag) state.dataUnitHeaters->MyEnvrnFlag(UnitHeatNum) = true;
     712              : 
     713              :         // These initializations are done every iteration...
     714          363 :         InNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
     715          363 :         OutNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
     716              : 
     717          363 :         state.dataUnitHeaters->QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // zone load needed
     718          363 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched != nullptr) {
     719            0 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched->getCurrentVal() == 0.0 &&
     720            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanType == HVAC::FanType::OnOff) {
     721            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Cycling;
     722              :             } else {
     723            0 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Continuous;
     724              :             }
     725            0 :             if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
     726              :                 // Unit is available, but there is no load on it or we are in setback/deadband
     727            0 :                 if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
     728            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOpModeSched->getCurrentVal() > 0.0) {
     729            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp = HVAC::FanOp::Continuous;
     730              :                 }
     731              :             }
     732              :         }
     733              : 
     734          363 :         state.dataUnitHeaters->SetMassFlowRateToZero = false;
     735          363 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() > 0) {
     736          726 :             if ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() > 0 || state.dataHVACGlobal->TurnFansOn) &&
     737          363 :                 !state.dataHVACGlobal->TurnFansOff) {
     738          726 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating &&
     739          363 :                     ((state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP < HVAC::SmallLoad) ||
     740            3 :                      (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)))) {
     741          360 :                     state.dataUnitHeaters->SetMassFlowRateToZero = true;
     742              :                 }
     743              :             } else {
     744            0 :                 state.dataUnitHeaters->SetMassFlowRateToZero = true;
     745              :             }
     746              :         } else {
     747            0 :             state.dataUnitHeaters->SetMassFlowRateToZero = true;
     748              :         }
     749              : 
     750          363 :         if (state.dataUnitHeaters->SetMassFlowRateToZero) {
     751          360 :             state.dataLoopNodes->Node(InNode).MassFlowRate = 0.0;
     752          360 :             state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = 0.0;
     753          360 :             state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = 0.0;
     754          360 :             state.dataLoopNodes->Node(OutNode).MassFlowRate = 0.0;
     755          360 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = 0.0;
     756          360 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = 0.0;
     757              :         } else {
     758            3 :             state.dataLoopNodes->Node(InNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     759            3 :             state.dataLoopNodes->Node(InNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     760            3 :             state.dataLoopNodes->Node(InNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     761            3 :             state.dataLoopNodes->Node(OutNode).MassFlowRate = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     762            3 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMaxAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     763            3 :             state.dataLoopNodes->Node(OutNode).MassFlowRateMinAvail = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirMassFlow;
     764              :         }
     765              : 
     766              :         // Just in case the unit is off and conditions do not get sent through
     767              :         // the unit for some reason, set the outlet conditions equal to the inlet
     768              :         // conditions of the unit heater
     769          363 :         state.dataLoopNodes->Node(OutNode).Temp = state.dataLoopNodes->Node(InNode).Temp;
     770          363 :         state.dataLoopNodes->Node(OutNode).Press = state.dataLoopNodes->Node(InNode).Press;
     771          363 :         state.dataLoopNodes->Node(OutNode).HumRat = state.dataLoopNodes->Node(InNode).HumRat;
     772          363 :         state.dataLoopNodes->Node(OutNode).Enthalpy = state.dataLoopNodes->Node(InNode).Enthalpy;
     773          363 :     }
     774              : 
     775            2 :     void SizeUnitHeater(EnergyPlusData &state, int const UnitHeatNum)
     776              :     {
     777              : 
     778              :         // SUBROUTINE INFORMATION:
     779              :         //       AUTHOR         Fred Buhl
     780              :         //       DATE WRITTEN   February 2002
     781              :         //       MODIFIED       August 2013 Daeho Kang, add component sizing table entries
     782              :         //                      July 2014, B. Nigusse, added scalable sizing
     783              :         //       RE-ENGINEERED  na
     784              : 
     785              :         // PURPOSE OF THIS SUBROUTINE:
     786              :         // This subroutine is for sizing Unit Heater components for which flow rates have not been
     787              :         // specified in the input.
     788              : 
     789              :         // METHODOLOGY EMPLOYED:
     790              :         // Obtains flow rates from the zone sizing arrays and plant sizing data.
     791              : 
     792              :         // SUBROUTINE PARAMETER DEFINITIONS:
     793              :         static constexpr std::string_view RoutineName("SizeUnitHeater");
     794              : 
     795              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     796              :         int PltSizHeatNum; // index of plant sizing object for 1st heating loop
     797              :         Real64 DesCoilLoad;
     798              :         Real64 TempSteamIn;
     799              :         Real64 EnthSteamInDry;
     800              :         Real64 EnthSteamOutWet;
     801              :         Real64 LatentHeatSteam;
     802              :         Real64 SteamDensity;
     803              :         Real64 Cp;                 // local temporary for fluid specific heat
     804              :         Real64 rho;                // local temporary for fluid density
     805            2 :         std::string SizingString;  // input field sizing description (e.g., Nominal Capacity)
     806              :         Real64 TempSize;           // autosized value of coil input field
     807              :         bool PrintFlag;            // TRUE when sizing information is reported in the eio file
     808              :         int zoneHVACIndex;         // index of zoneHVAC equipment sizing specification
     809              :         Real64 WaterCoilSizDeltaT; // water coil deltaT for design water flow rate autosizing
     810              : 
     811            2 :         int &CurZoneEqNum = state.dataSize->CurZoneEqNum;
     812              : 
     813            2 :         bool ErrorsFound = false;
     814            2 :         Real64 MaxAirVolFlowDes = 0.0;
     815            2 :         Real64 MaxAirVolFlowUser = 0.0;
     816            2 :         Real64 MaxVolHotWaterFlowDes = 0.0;
     817            2 :         Real64 MaxVolHotWaterFlowUser = 0.0;
     818            2 :         Real64 MaxVolHotSteamFlowDes = 0.0;
     819            2 :         Real64 MaxVolHotSteamFlowUser = 0.0;
     820              : 
     821            2 :         state.dataSize->DataScalableSizingON = false;
     822            2 :         state.dataSize->DataScalableCapSizingON = false;
     823            2 :         state.dataSize->ZoneHeatingOnlyFan = true;
     824            2 :         std::string CompType = "ZoneHVAC:UnitHeater";
     825            2 :         std::string CompName = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name;
     826            2 :         state.dataSize->DataZoneNumber = state.dataUnitHeaters->UnitHeat(UnitHeatNum).ZonePtr;
     827            2 :         state.dataSize->DataFanType = state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanType;
     828            2 :         state.dataSize->DataFanIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index;
     829              :         // unit heater is always blow thru
     830            2 :         state.dataSize->DataFanPlacement = HVAC::FanPlace::BlowThru;
     831              : 
     832            2 :         if (CurZoneEqNum > 0) {
     833            2 :             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
     834            2 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
     835            0 :                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
     836            0 :                 int SizingMethod = HVAC::HeatingAirflowSizing;
     837            0 :                 int FieldNum = 1; //  N1 , \field Maximum Supply Air Flow Rate
     838            0 :                 PrintFlag = true;
     839            0 :                 SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
     840            0 :                 int SAFMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingSAFMethod;
     841            0 :                 ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
     842            0 :                 if (SAFMethod == DataSizing::None || SAFMethod == DataSizing::SupplyAirFlowRate || SAFMethod == DataSizing::FlowPerFloorArea ||
     843              :                     SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
     844            0 :                     if (SAFMethod == DataSizing::SupplyAirFlowRate) {
     845            0 :                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow > 0.0) {
     846            0 :                             ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     847            0 :                             ZoneEqSizing.SystemAirFlow = true;
     848              :                         }
     849            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     850            0 :                     } else if (SAFMethod == DataSizing::FlowPerFloorArea) {
     851            0 :                         ZoneEqSizing.SystemAirFlow = true;
     852            0 :                         ZoneEqSizing.AirVolFlow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow *
     853            0 :                                                   state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
     854            0 :                         TempSize = ZoneEqSizing.AirVolFlow;
     855            0 :                         state.dataSize->DataScalableSizingON = true;
     856            0 :                     } else if (SAFMethod == DataSizing::FractionOfAutosizedHeatingAirflow) {
     857            0 :                         state.dataSize->DataFracOfAutosizedCoolingAirflow = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     858            0 :                         TempSize = DataSizing::AutoSize;
     859            0 :                         state.dataSize->DataScalableSizingON = true;
     860              :                     } else {
     861            0 :                         TempSize = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     862              :                     }
     863            0 :                     bool errorsFound = false;
     864            0 :                     HeatingAirFlowSizer sizingHeatingAirFlow;
     865            0 :                     sizingHeatingAirFlow.overrideSizingString(SizingString);
     866              :                     // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     867            0 :                     sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     868            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     869              : 
     870            0 :                 } else if (SAFMethod == DataSizing::FlowPerHeatingCapacity) {
     871            0 :                     TempSize = DataSizing::AutoSize;
     872            0 :                     PrintFlag = false;
     873            0 :                     state.dataSize->DataScalableSizingON = true;
     874            0 :                     state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatVolFlow;
     875            0 :                     bool errorsFound = false;
     876            0 :                     HeatingCapacitySizer sizerHeatingCapacity;
     877            0 :                     sizerHeatingCapacity.overrideSizingString(SizingString);
     878            0 :                     sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     879            0 :                     TempSize = sizerHeatingCapacity.size(state, TempSize, errorsFound);
     880            0 :                     if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     881            0 :                         state.dataSize->DataFracOfAutosizedHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     882              :                     }
     883            0 :                     state.dataSize->DataAutosizedHeatingCapacity = TempSize;
     884            0 :                     state.dataSize->DataFlowPerHeatingCapacity = state.dataSize->ZoneHVACSizing(zoneHVACIndex).MaxHeatAirVolFlow;
     885            0 :                     PrintFlag = true;
     886            0 :                     TempSize = DataSizing::AutoSize;
     887            0 :                     errorsFound = false;
     888            0 :                     HeatingAirFlowSizer sizingHeatingAirFlow;
     889            0 :                     sizingHeatingAirFlow.overrideSizingString(SizingString);
     890              :                     // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     891            0 :                     sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     892            0 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     893            0 :                 }
     894            0 :                 state.dataSize->DataScalableSizingON = false;
     895              :             } else {
     896              :                 // no scalble sizing method has been specified. Sizing proceeds using the method
     897              :                 // specified in the zoneHVAC object
     898            2 :                 int FieldNum = 1; // N1 , \field Maximum Supply Air Flow Rate
     899            2 :                 PrintFlag = true;
     900            2 :                 SizingString = state.dataUnitHeaters->UnitHeatNumericFields(UnitHeatNum).FieldNames(FieldNum) + " [m3/s]";
     901            2 :                 TempSize = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow;
     902            2 :                 bool errorsFound = false;
     903            2 :                 HeatingAirFlowSizer sizingHeatingAirFlow;
     904            2 :                 sizingHeatingAirFlow.overrideSizingString(SizingString);
     905              :                 // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
     906            2 :                 sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     907            2 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
     908            2 :             }
     909              :         }
     910              : 
     911            2 :         bool IsAutoSize = false;
     912            2 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow == DataSizing::AutoSize) {
     913            1 :             IsAutoSize = true;
     914              :         }
     915              : 
     916            2 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
     917              : 
     918            1 :             if (CurZoneEqNum > 0) {
     919            1 :                 if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
     920            0 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0) {
     921            0 :                         BaseSizer::reportSizerOutput(state,
     922              :                                                      "ZoneHVAC:UnitHeater",
     923            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
     924              :                                                      "User-Specified Maximum Hot Water Flow [m3/s]",
     925            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow);
     926              :                     }
     927              :                 } else {
     928            1 :                     CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
     929              : 
     930            1 :                     int CoilWaterInletNode = WaterCoils::GetCoilWaterInletNode(
     931            1 :                         state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     932            1 :                     int CoilWaterOutletNode = WaterCoils::GetCoilWaterOutletNode(
     933            1 :                         state, "Coil:Heating:Water", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     934            1 :                     if (IsAutoSize) {
     935            1 :                         bool DoWaterCoilSizing = false; // if TRUE do water coil sizing calculation
     936            1 :                         PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(state,
     937              :                                                                            "Coil:Heating:Water",
     938            1 :                                                                            state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
     939              :                                                                            CoilWaterInletNode,
     940              :                                                                            CoilWaterOutletNode,
     941              :                                                                            ErrorsFound);
     942            1 :                         int CoilNum = WaterCoils::GetWaterCoilIndex(
     943            1 :                             state, "COIL:HEATING:WATER", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
     944            1 :                         if (state.dataWaterCoils->WaterCoil(CoilNum).UseDesignWaterDeltaTemp) {
     945            0 :                             WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(CoilNum).DesignWaterDeltaTemp;
     946            0 :                             DoWaterCoilSizing = true;
     947              :                         } else {
     948            1 :                             if (PltSizHeatNum > 0) {
     949            1 :                                 WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizHeatNum).DeltaT;
     950            1 :                                 DoWaterCoilSizing = true;
     951              :                             } else {
     952            0 :                                 DoWaterCoilSizing = false;
     953              :                                 // If there is no heating Plant Sizing object and autosizing was requested, issue fatal error message
     954            0 :                                 ShowSevereError(state, "Autosizing of water coil requires a heating loop Sizing:Plant object");
     955            0 :                                 ShowContinueError(
     956            0 :                                     state, format("Occurs in ZoneHVAC:UnitHeater Object={}", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
     957            0 :                                 ErrorsFound = true;
     958              :                             }
     959              :                         }
     960              : 
     961            1 :                         if (DoWaterCoilSizing) {
     962            1 :                             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
     963            1 :                             int SizingMethod = HVAC::HeatingCapacitySizing;
     964            1 :                             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
     965            0 :                                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
     966            0 :                                 int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
     967            0 :                                 ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
     968            0 :                                 if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
     969              :                                     CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     970            0 :                                     if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
     971            0 :                                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == DataSizing::AutoSize) {
     972            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
     973              :                                         } else {
     974            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     975              :                                         }
     976            0 :                                         ZoneEqSizing.HeatingCapacity = true;
     977            0 :                                         TempSize = DataSizing::AutoSize;
     978            0 :                                     } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
     979            0 :                                         ZoneEqSizing.HeatingCapacity = true;
     980            0 :                                         ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
     981            0 :                                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
     982            0 :                                         state.dataSize->DataScalableCapSizingON = true;
     983            0 :                                     } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
     984            0 :                                         state.dataSize->DataFracOfAutosizedHeatingCapacity =
     985            0 :                                             state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
     986            0 :                                         state.dataSize->DataScalableCapSizingON = true;
     987            0 :                                         TempSize = DataSizing::AutoSize;
     988              :                                     }
     989              :                                 }
     990            0 :                                 PrintFlag = false;
     991            0 :                                 bool errorsFound = false;
     992            0 :                                 HeatingCapacitySizer sizerHeatingCapacity;
     993            0 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
     994            0 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     995            0 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
     996            0 :                                 state.dataSize->DataScalableCapSizingON = false;
     997            0 :                             } else {
     998            1 :                                 SizingString = "";
     999            1 :                                 PrintFlag = false;
    1000            1 :                                 TempSize = DataSizing::AutoSize;
    1001            1 :                                 ZoneEqSizing.HeatingCapacity = true;
    1002            1 :                                 ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1003            1 :                                 bool errorsFound = false;
    1004            1 :                                 HeatingCapacitySizer sizerHeatingCapacity;
    1005            1 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1006            1 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1007            1 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
    1008            1 :                             }
    1009              : 
    1010            1 :                             if (DesCoilLoad >= HVAC::SmallLoad) {
    1011            1 :                                 rho = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
    1012            1 :                                           .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
    1013            1 :                                 Cp = state.dataPlnt->PlantLoop(state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum)
    1014            1 :                                          .glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineName);
    1015            1 :                                 MaxVolHotWaterFlowDes = DesCoilLoad / (WaterCoilSizDeltaT * Cp * rho);
    1016              :                             } else {
    1017            0 :                                 MaxVolHotWaterFlowDes = 0.0;
    1018              :                             }
    1019              :                         }
    1020            1 :                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
    1021            2 :                         BaseSizer::reportSizerOutput(state,
    1022              :                                                      "ZoneHVAC:UnitHeater",
    1023            1 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1024              :                                                      "Design Size Maximum Hot Water Flow [m3/s]",
    1025              :                                                      MaxVolHotWaterFlowDes);
    1026              :                     } else {
    1027            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
    1028            0 :                             MaxVolHotWaterFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
    1029            0 :                             BaseSizer::reportSizerOutput(state,
    1030              :                                                          "ZoneHVAC:UnitHeater",
    1031            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1032              :                                                          "Design Size Maximum Hot Water Flow [m3/s]",
    1033              :                                                          MaxVolHotWaterFlowDes,
    1034              :                                                          "User-Specified Maximum Hot Water Flow [m3/s]",
    1035              :                                                          MaxVolHotWaterFlowUser);
    1036            0 :                             if (state.dataGlobal->DisplayExtraWarnings) {
    1037            0 :                                 if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
    1038            0 :                                     state.dataSize->AutoVsHardSizingThreshold) {
    1039            0 :                                     ShowMessage(state,
    1040            0 :                                                 format("SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater {}",
    1041            0 :                                                        state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1042            0 :                                     ShowContinueError(state,
    1043            0 :                                                       format("User-Specified Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
    1044            0 :                                     ShowContinueError(
    1045            0 :                                         state, format("differs from Design Size Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
    1046            0 :                                     ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1047            0 :                                     ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1048              :                                 }
    1049              :                             }
    1050              :                         }
    1051              :                     }
    1052              :                 }
    1053              :             }
    1054              :         } else {
    1055            1 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow = 0.0;
    1056              :         }
    1057              : 
    1058            2 :         IsAutoSize = false;
    1059            2 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow == DataSizing::AutoSize) {
    1060            1 :             IsAutoSize = true;
    1061              :         }
    1062              : 
    1063            2 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1064              : 
    1065            0 :             if (CurZoneEqNum > 0) {
    1066            0 :                 if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1067            0 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0) {
    1068            0 :                         BaseSizer::reportSizerOutput(state,
    1069              :                                                      "ZoneHVAC:UnitHeater",
    1070            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1071              :                                                      "User-Specified Maximum Steam Flow [m3/s]",
    1072            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow);
    1073              :                     }
    1074              :                 } else {
    1075            0 :                     auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
    1076            0 :                     CheckZoneSizing(state, "ZoneHVAC:UnitHeater", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name);
    1077              : 
    1078            0 :                     int CoilSteamInletNode = SteamCoils::GetCoilSteamInletNode(
    1079            0 :                         state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
    1080            0 :                     int CoilSteamOutletNode = SteamCoils::GetCoilSteamInletNode(
    1081            0 :                         state, "Coil:Heating:Steam", state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName, ErrorsFound);
    1082            0 :                     if (IsAutoSize) {
    1083            0 :                         PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(state,
    1084              :                                                                            "Coil:Heating:Steam",
    1085            0 :                                                                            state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1086              :                                                                            CoilSteamInletNode,
    1087              :                                                                            CoilSteamOutletNode,
    1088              :                                                                            ErrorsFound);
    1089            0 :                         if (PltSizHeatNum > 0) {
    1090            0 :                             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex > 0) {
    1091            0 :                                 zoneHVACIndex = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HVACSizingIndex;
    1092            0 :                                 int SizingMethod = HVAC::HeatingCapacitySizing;
    1093            0 :                                 int CapSizingMethod = state.dataSize->ZoneHVACSizing(zoneHVACIndex).HeatingCapMethod;
    1094            0 :                                 ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
    1095            0 :                                 if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
    1096              :                                     CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
    1097            0 :                                     if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
    1098            0 :                                         if (state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity == DataSizing::AutoSize) {
    1099            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1100              :                                         } else {
    1101            0 :                                             ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1102              :                                         }
    1103            0 :                                         ZoneEqSizing.HeatingCapacity = true;
    1104            0 :                                         TempSize = DataSizing::AutoSize;
    1105            0 :                                     } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
    1106            0 :                                         ZoneEqSizing.HeatingCapacity = true;
    1107            0 :                                         ZoneEqSizing.DesHeatingLoad = state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity *
    1108            0 :                                                                       state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
    1109            0 :                                         state.dataSize->DataScalableCapSizingON = true;
    1110            0 :                                     } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
    1111            0 :                                         state.dataSize->DataFracOfAutosizedHeatingCapacity =
    1112            0 :                                             state.dataSize->ZoneHVACSizing(zoneHVACIndex).ScaledHeatingCapacity;
    1113            0 :                                         TempSize = DataSizing::AutoSize;
    1114            0 :                                         state.dataSize->DataScalableCapSizingON = true;
    1115              :                                     }
    1116              :                                 }
    1117            0 :                                 PrintFlag = false;
    1118            0 :                                 bool errorsFound = false;
    1119            0 :                                 HeatingCapacitySizer sizerHeatingCapacity;
    1120            0 :                                 sizerHeatingCapacity.overrideSizingString(SizingString);
    1121            0 :                                 sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
    1122            0 :                                 DesCoilLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
    1123            0 :                                 state.dataSize->DataScalableCapSizingON = false;
    1124            0 :                             } else {
    1125            0 :                                 DesCoilLoad = state.dataSize->FinalZoneSizing(CurZoneEqNum).DesHeatLoad;
    1126              :                             }
    1127            0 :                             if (DesCoilLoad >= HVAC::SmallLoad) {
    1128            0 :                                 TempSteamIn = 100.00;
    1129            0 :                                 auto *steam = Fluid::GetSteam(state);
    1130            0 :                                 EnthSteamInDry = steam->getSatEnthalpy(state, TempSteamIn, 1.0, RoutineName);
    1131            0 :                                 EnthSteamOutWet = steam->getSatEnthalpy(state, TempSteamIn, 0.0, RoutineName);
    1132            0 :                                 LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
    1133            0 :                                 SteamDensity = steam->getSatDensity(state, TempSteamIn, 1.0, RoutineName);
    1134            0 :                                 MaxVolHotSteamFlowDes =
    1135            0 :                                     DesCoilLoad / (SteamDensity * (LatentHeatSteam +
    1136            0 :                                                                    state.dataSize->PlantSizData(PltSizHeatNum).DeltaT *
    1137            0 :                                                                        Psychrometrics::CPHW(state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp)));
    1138              :                             } else {
    1139            0 :                                 MaxVolHotSteamFlowDes = 0.0;
    1140              :                             }
    1141              :                         } else {
    1142            0 :                             ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
    1143            0 :                             ShowContinueError(state,
    1144            0 :                                               format("Occurs in ZoneHVAC:UnitHeater Object={}", state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1145            0 :                             ErrorsFound = true;
    1146              :                         }
    1147            0 :                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
    1148            0 :                         BaseSizer::reportSizerOutput(state,
    1149              :                                                      "ZoneHVAC:UnitHeater",
    1150            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1151              :                                                      "Design Size Maximum Steam Flow [m3/s]",
    1152              :                                                      MaxVolHotSteamFlowDes);
    1153              :                     } else {
    1154            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
    1155            0 :                             MaxVolHotSteamFlowUser = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow;
    1156            0 :                             BaseSizer::reportSizerOutput(state,
    1157              :                                                          "ZoneHVAC:UnitHeater",
    1158            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1159              :                                                          "Design Size Maximum Steam Flow [m3/s]",
    1160              :                                                          MaxVolHotSteamFlowDes,
    1161              :                                                          "User-Specified Maximum Steam Flow [m3/s]",
    1162              :                                                          MaxVolHotSteamFlowUser);
    1163            0 :                             if (state.dataGlobal->DisplayExtraWarnings) {
    1164            0 :                                 if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
    1165            0 :                                     state.dataSize->AutoVsHardSizingThreshold) {
    1166            0 :                                     ShowMessage(state,
    1167            0 :                                                 format("SizeUnitHeater: Potential issue with equipment sizing for ZoneHVAC:UnitHeater {}",
    1168            0 :                                                        state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name));
    1169            0 :                                     ShowContinueError(state, format("User-Specified Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
    1170            0 :                                     ShowContinueError(state,
    1171            0 :                                                       format("differs from Design Size Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
    1172            0 :                                     ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1173            0 :                                     ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1174              :                                 }
    1175              :                             }
    1176              :                         }
    1177              :                     }
    1178              :                 }
    1179              :             }
    1180              :         } else {
    1181            2 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotSteamFlow = 0.0;
    1182              :         }
    1183              : 
    1184              :         // set the design air flow rate for the heating coil
    1185              : 
    1186            2 :         WaterCoils::SetCoilDesFlow(state,
    1187            2 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilTypeCh,
    1188            2 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1189            2 :                                    state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxAirVolFlow,
    1190              :                                    ErrorsFound);
    1191            2 :         if (CurZoneEqNum > 0) {
    1192            2 :             auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(CurZoneEqNum);
    1193            2 :             ZoneEqSizing.MaxHWVolFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxVolHotWaterFlow;
    1194              :         }
    1195              : 
    1196            2 :         if (ErrorsFound) {
    1197            0 :             ShowFatalError(state, "Preceding sizing errors cause program termination");
    1198              :         }
    1199            2 :     }
    1200              : 
    1201          362 :     void CalcUnitHeater(EnergyPlusData &state,
    1202              :                         int &UnitHeatNum,              // number of the current fan coil unit being simulated
    1203              :                         int const ZoneNum,             // number of zone being served
    1204              :                         bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
    1205              :                         Real64 &PowerMet,              // Sensible power supplied (W)
    1206              :                         Real64 &LatOutputProvided      // Latent power supplied (kg/s), negative = dehumidification
    1207              :     )
    1208              :     {
    1209              : 
    1210              :         // SUBROUTINE INFORMATION:
    1211              :         //       AUTHOR         Rick Strand
    1212              :         //       DATE WRITTEN   May 2000
    1213              :         //       MODIFIED       Don Shirey, Aug 2009 (LatOutputProvided)
    1214              :         //                      July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
    1215              : 
    1216              :         // PURPOSE OF THIS SUBROUTINE:
    1217              :         // This subroutine mainly controls the action of the unit heater
    1218              :         // based on the user input for controls and the defined controls
    1219              :         // algorithms.  There are currently (at the initial creation of this
    1220              :         // subroutine) two control methods: on-off fan operation or continuous
    1221              :         // fan operation.
    1222              : 
    1223              :         // METHODOLOGY EMPLOYED:
    1224              :         // Unit is controlled based on user input and what is happening in the
    1225              :         // simulation.  There are various cases to consider:
    1226              :         // 1. OFF: Unit is schedule off.  All flow rates are set to zero and
    1227              :         //    the temperatures are set to zone conditions.
    1228              :         // 2. NO LOAD OR COOLING/ON-OFF FAN CONTROL: Unit is available, but
    1229              :         //    there is no heating load.  All flow rates are set to zero and
    1230              :         //    the temperatures are set to zone conditions.
    1231              :         // 3. NO LOAD OR COOLING/CONTINUOUS FAN CONTROL: Unit is available and
    1232              :         //    the fan is running (if it is scheduled to be available also).
    1233              :         //    No heating is provided, only circulation via the fan running.
    1234              :         // 4. HEATING: The unit is on/available and there is a heating load.
    1235              :         //    The heating coil is modulated (constant fan speed) to meet the
    1236              :         //    heating load.
    1237              : 
    1238              :         // REFERENCES:
    1239              :         // ASHRAE Systems and Equipment Handbook (SI), 1996. page 31.7
    1240              : 
    1241              :         // SUBROUTINE PARAMETER DEFINITIONS:
    1242          362 :         int constexpr MaxIter = 100; // maximum number of iterations
    1243              : 
    1244              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1245              :         Real64 SpecHumOut; // Specific humidity ratio of outlet air (kg moisture / kg moist air)
    1246              :         Real64 SpecHumIn;  // Specific humidity ratio of inlet air (kg moisture / kg moist air)
    1247              :         Real64 mdot;       // local temporary for fluid mass flow rate
    1248              : 
    1249              :         // initialize local variables
    1250          362 :         Real64 QUnitOut = 0.0;
    1251          362 :         Real64 NoOutput = 0.0;
    1252          362 :         Real64 FullOutput = 0.0;
    1253          362 :         Real64 LatentOutput = 0.0; // Latent (moisture) add/removal rate, negative is dehumidification [kg/s]
    1254          362 :         Real64 MaxWaterFlow = 0.0;
    1255          362 :         Real64 MinWaterFlow = 0.0;
    1256          362 :         Real64 PartLoadFrac = 0.0;
    1257          362 :         int InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
    1258          362 :         int OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
    1259          362 :         int ControlNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode;
    1260          362 :         Real64 ControlOffset = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlOffset;
    1261          362 :         HVAC::FanOp fanOp = state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanOp;
    1262              : 
    1263          362 :         if (fanOp != HVAC::FanOp::Cycling) {
    1264              : 
    1265            9 :             if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() <= 0 ||
    1266            3 :                 ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() <= 0 && !state.dataHVACGlobal->TurnFansOn) ||
    1267            3 :                  state.dataHVACGlobal->TurnFansOff)) {
    1268              :                 // Case 1: OFF-->unit schedule says that it it not available
    1269              :                 //         OR child fan in not available OR child fan not being cycled ON by sys avail manager
    1270              :                 //         OR child fan being forced OFF by sys avail manager
    1271            0 :                 state.dataUnitHeaters->HCoilOn = false;
    1272            0 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1273            0 :                     mdot = 0.0; // try to turn off
    1274              : 
    1275            0 :                     PlantUtilities::SetComponentFlowRate(state,
    1276              :                                                          mdot,
    1277            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1278            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1279            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1280              :                 }
    1281            0 :                 if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1282            0 :                     mdot = 0.0; // try to turn off
    1283              : 
    1284            0 :                     PlantUtilities::SetComponentFlowRate(state,
    1285              :                                                          mdot,
    1286            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1287            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1288            0 :                                                          state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1289              :                 }
    1290            0 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1291              : 
    1292            3 :             } else if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) {
    1293              :                 // Unit is available, but there is no load on it or we are in setback/deadband
    1294            1 :                 if (!state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOffNoHeating) {
    1295              : 
    1296              :                     // Case 2: NO LOAD OR COOLING/ON-OFF FAN CONTROL-->turn everything off
    1297              :                     //         because there is no load on the unit heater
    1298            0 :                     state.dataUnitHeaters->HCoilOn = false;
    1299            0 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1300            0 :                         mdot = 0.0; // try to turn off
    1301              : 
    1302            0 :                         PlantUtilities::SetComponentFlowRate(state,
    1303              :                                                              mdot,
    1304            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1305            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1306            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1307              :                     }
    1308            0 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1309            0 :                         mdot = 0.0; // try to turn off
    1310              : 
    1311            0 :                         PlantUtilities::SetComponentFlowRate(state,
    1312              :                                                              mdot,
    1313            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1314            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1315            0 :                                                              state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1316              :                     }
    1317            0 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1318              : 
    1319              :                 } else {
    1320              :                     // Case 3: NO LOAD OR COOLING/CONTINUOUS FAN CONTROL-->let the fan
    1321              :                     //         continue to run even though there is no load (air circulation)
    1322              :                     // Note that the flow rates were already set in the initialization routine
    1323              :                     // so there is really nothing else left to do except call the components.
    1324              : 
    1325            1 :                     state.dataUnitHeaters->HCoilOn = false;
    1326            1 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::WaterHeatingCoil) {
    1327            1 :                         mdot = 0.0; // try to turn off
    1328              : 
    1329            1 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
    1330            2 :                             PlantUtilities::SetComponentFlowRate(state,
    1331              :                                                                  mdot,
    1332            1 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1333            1 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1334            1 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1335              :                         }
    1336              :                     }
    1337            1 :                     if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type == HCoilType::SteamCoil) {
    1338            0 :                         mdot = 0.0; // try to turn off
    1339            0 :                         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc.loopNum > 0) {
    1340            0 :                             PlantUtilities::SetComponentFlowRate(state,
    1341              :                                                                  mdot,
    1342            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1343            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1344            0 :                                                                  state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1345              :                         }
    1346              :                     }
    1347              : 
    1348            1 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1349              :                 }
    1350              : 
    1351              :             } else { // Case 4: HEATING-->unit is available and there is a heating load
    1352              : 
    1353            2 :                 switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1354              : 
    1355            2 :                 case HCoilType::WaterHeatingCoil: {
    1356              : 
    1357              :                     // On the first HVAC iteration the system values are given to the controller, but after that
    1358              :                     // the demand limits are in place and there needs to be feedback to the Zone Equipment
    1359            2 :                     if (FirstHVACIteration) {
    1360            2 :                         MaxWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow;
    1361            2 :                         MinWaterFlow = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MinHotWaterFlow;
    1362              :                     } else {
    1363            0 :                         MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
    1364            0 :                         MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
    1365              :                     }
    1366              :                     // control water flow to obtain output matching QZnReq
    1367            6 :                     ControlCompOutput(state,
    1368            2 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name,
    1369            2 :                                       state.dataUnitHeaters->cMO_UnitHeater,
    1370              :                                       UnitHeatNum,
    1371              :                                       FirstHVACIteration,
    1372            2 :                                       state.dataUnitHeaters->QZnReq,
    1373              :                                       ControlNode,
    1374              :                                       MaxWaterFlow,
    1375              :                                       MinWaterFlow,
    1376              :                                       ControlOffset,
    1377            2 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).ControlCompTypeNum,
    1378            2 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).CompErrIndex,
    1379              :                                       _,
    1380              :                                       _,
    1381              :                                       _,
    1382              :                                       _,
    1383              :                                       _,
    1384            2 :                                       state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1385            2 :                     break;
    1386              :                 }
    1387            0 :                 case HCoilType::Electric:
    1388              :                 case HCoilType::Gas:
    1389              :                 case HCoilType::SteamCoil: {
    1390            0 :                     state.dataUnitHeaters->HCoilOn = true;
    1391            0 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut);
    1392            0 :                     break;
    1393              :                 }
    1394            0 :                 default:
    1395            0 :                     break;
    1396              :                 }
    1397              :             }
    1398            3 :             if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
    1399            2 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
    1400            2 :                     state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
    1401              :             }
    1402              :         } else { // OnOff fan and cycling
    1403          361 :             if ((state.dataUnitHeaters->QZnReq < HVAC::SmallLoad) || (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) ||
    1404          362 :                 state.dataUnitHeaters->UnitHeat(UnitHeatNum).availSched->getCurrentVal() <= 0 ||
    1405            1 :                 ((state.dataUnitHeaters->UnitHeat(UnitHeatNum).fanAvailSched->getCurrentVal() <= 0 && !state.dataHVACGlobal->TurnFansOn) ||
    1406            1 :                  state.dataHVACGlobal->TurnFansOff)) {
    1407              :                 // Case 1: OFF-->unit schedule says that it it not available
    1408              :                 //         OR child fan in not available OR child fan not being cycled ON by sys avail manager
    1409              :                 //         OR child fan being forced OFF by sys avail manager
    1410          358 :                 PartLoadFrac = 0.0;
    1411          358 :                 state.dataUnitHeaters->HCoilOn = false;
    1412          358 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
    1413              : 
    1414          358 :                 if (state.dataLoopNodes->Node(InletNode).MassFlowRateMax > 0.0) {
    1415          358 :                     state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio =
    1416          358 :                         state.dataLoopNodes->Node(InletNode).MassFlowRate / state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
    1417              :                 }
    1418              : 
    1419              :             } else { // Case 4: HEATING-->unit is available and there is a heating load
    1420              : 
    1421            1 :                 state.dataUnitHeaters->HCoilOn = true;
    1422              : 
    1423              :                 // Find part load ratio of unit heater coils
    1424            1 :                 PartLoadFrac = 0.0;
    1425            1 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, NoOutput, fanOp, PartLoadFrac);
    1426            1 :                 if ((NoOutput - state.dataUnitHeaters->QZnReq) < HVAC::SmallLoad) {
    1427              :                     // Unit heater is unable to meet the load with coil off, set PLR = 1
    1428            1 :                     PartLoadFrac = 1.0;
    1429            1 :                     CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, FullOutput, fanOp, PartLoadFrac);
    1430            1 :                     if ((FullOutput - state.dataUnitHeaters->QZnReq) > HVAC::SmallLoad) {
    1431              :                         // Unit heater full load capacity is able to meet the load, Find PLR
    1432              : 
    1433            0 :                         auto f = [&state, UnitHeatNum, FirstHVACIteration, fanOp](Real64 const PartLoadRatio) {
    1434              :                             // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1435              :                             Real64 QUnitOut; // heating provided by unit heater [watts]
    1436              : 
    1437            0 :                             CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadRatio);
    1438              : 
    1439              :                             // Calculate residual based on output calculation flag
    1440            0 :                             if (state.dataUnitHeaters->QZnReq != 0.0) {
    1441            0 :                                 return (QUnitOut - state.dataUnitHeaters->QZnReq) / state.dataUnitHeaters->QZnReq;
    1442              :                             } else
    1443            0 :                                 return 0.0;
    1444            0 :                         };
    1445              : 
    1446              :                         // Tolerance is in fraction of load, MaxIter = 30, SolFalg = # of iterations or error as appropriate
    1447            0 :                         int SolFlag = 0; // # of iterations IF positive, -1 means failed to converge, -2 means bounds are incorrect
    1448            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadFrac, f, 0.0, 1.0);
    1449              :                     }
    1450              :                 }
    1451              : 
    1452            1 :                 CalcUnitHeaterComponents(state, UnitHeatNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
    1453              : 
    1454              :             } // ...end of unit ON/OFF IF-THEN block
    1455          359 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).PartLoadFrac = PartLoadFrac;
    1456          359 :             state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanPartLoadRatio = PartLoadFrac;
    1457          359 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    1458              :         }
    1459              : 
    1460              :         // CR9155 Remove specific humidity calculations
    1461          362 :         SpecHumOut = state.dataLoopNodes->Node(OutletNode).HumRat;
    1462          362 :         SpecHumIn = state.dataLoopNodes->Node(InletNode).HumRat;
    1463          362 :         LatentOutput = state.dataLoopNodes->Node(OutletNode).MassFlowRate * (SpecHumOut - SpecHumIn); // Latent rate (kg/s), dehumid = negative
    1464              : 
    1465          362 :         QUnitOut = state.dataLoopNodes->Node(OutletNode).MassFlowRate *
    1466          362 :                    (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
    1467          362 :                     Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
    1468              : 
    1469              :         // Report variables...
    1470          362 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower = max(0.0, QUnitOut);
    1471          362 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower =
    1472          362 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->totalPower;
    1473              : 
    1474          362 :         PowerMet = QUnitOut;
    1475          362 :         LatOutputProvided = LatentOutput;
    1476          362 :     }
    1477              : 
    1478          377 :     void CalcUnitHeaterComponents(EnergyPlusData &state,
    1479              :                                   int const UnitHeatNum,         // Unit index in unit heater array
    1480              :                                   bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
    1481              :                                   Real64 &LoadMet,               // load met by unit (watts)
    1482              :                                   HVAC::FanOp const fanOp,       // fan operating mode
    1483              :                                   Real64 const PartLoadRatio     // part-load ratio
    1484              :     )
    1485              :     {
    1486              : 
    1487              :         // SUBROUTINE INFORMATION:
    1488              :         //       AUTHOR         Rick Strand
    1489              :         //       DATE WRITTEN   May 2000
    1490              :         //       MODIFIED       July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
    1491              : 
    1492              :         // PURPOSE OF THIS SUBROUTINE:
    1493              :         // This subroutine launches the individual component simulations.
    1494              :         // This is called either when the unit is off to carry null conditions
    1495              :         // through the unit or during control iterations to continue updating
    1496              :         // what is going on within the unit.
    1497              : 
    1498              :         // METHODOLOGY EMPLOYED:
    1499              :         // Simply calls the different components in order.
    1500              : 
    1501              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1502              :         Real64 AirMassFlow; // total mass flow through the unit
    1503              :         Real64 CpAirZn;     // specific heat of dry air at zone conditions (zone conditions same as unit inlet)
    1504              :         int HCoilInAirNode; // inlet node number for fan exit/coil inlet
    1505              :         Real64 mdot;        // local temporary for fluid mass flow rate
    1506              : 
    1507          377 :         int InletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode;
    1508          377 :         int OutletNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirOutNode;
    1509          377 :         Real64 QCoilReq = 0.0;
    1510              : 
    1511          377 :         if (fanOp != HVAC::FanOp::Cycling) {
    1512           16 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1513              : 
    1514           16 :             switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1515              : 
    1516           16 :             case HCoilType::WaterHeatingCoil: {
    1517              : 
    1518           32 :                 WaterCoils::SimulateWaterCoilComponents(state,
    1519           16 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1520              :                                                         FirstHVACIteration,
    1521           16 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
    1522           16 :                 break;
    1523              :             }
    1524            0 :             case HCoilType::SteamCoil: {
    1525              : 
    1526            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1527            0 :                     QCoilReq = 0.0;
    1528              :                 } else {
    1529            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1530            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1531            0 :                     QCoilReq =
    1532            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1533            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1534            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1535              :                 }
    1536            0 :                 if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1537            0 :                 SteamCoils::SimulateSteamCoilComponents(state,
    1538            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1539              :                                                         FirstHVACIteration,
    1540            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1541              :                                                         QCoilReq);
    1542            0 :                 break;
    1543              :             }
    1544            0 :             case HCoilType::Electric:
    1545              :             case HCoilType::Gas: {
    1546              : 
    1547            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1548            0 :                     QCoilReq = 0.0;
    1549              :                 } else {
    1550            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1551            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1552            0 :                     QCoilReq =
    1553            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1554            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1555            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1556              :                 }
    1557            0 :                 if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1558            0 :                 HeatingCoils::SimulateHeatingCoilComponents(state,
    1559            0 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1560              :                                                             FirstHVACIteration,
    1561              :                                                             QCoilReq,
    1562            0 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index);
    1563            0 :                 break;
    1564              :             }
    1565            0 :             default:
    1566            0 :                 break;
    1567              :             }
    1568              : 
    1569           16 :             AirMassFlow = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
    1570              : 
    1571           16 :             state.dataLoopNodes->Node(InletNode).MassFlowRate =
    1572           16 :                 state.dataLoopNodes->Node(OutletNode).MassFlowRate; // maintain continuity through unit heater
    1573              : 
    1574              :         } else { // OnOff fan cycling
    1575              : 
    1576          361 :             state.dataLoopNodes->Node(InletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRateMax * PartLoadRatio;
    1577          361 :             AirMassFlow = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    1578              :             // Set the fan inlet node maximum available mass flow rates for cycling fans
    1579          361 :             state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail = AirMassFlow;
    1580              : 
    1581          361 :             if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1582          361 :             state.dataFans->fans(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1583              : 
    1584          361 :             switch (state.dataUnitHeaters->UnitHeat(UnitHeatNum).Type) {
    1585              : 
    1586            0 :             case HCoilType::WaterHeatingCoil: {
    1587              : 
    1588            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1589            0 :                     mdot = 0.0;
    1590            0 :                     QCoilReq = 0.0;
    1591              :                 } else {
    1592            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1593            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1594            0 :                     QCoilReq =
    1595            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1596            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1597            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1598            0 :                     mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotWaterFlow * PartLoadRatio;
    1599              :                 }
    1600            0 :                 if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1601            0 :                 PlantUtilities::SetComponentFlowRate(state,
    1602              :                                                      mdot,
    1603            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1604            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1605            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1606            0 :                 WaterCoils::SimulateWaterCoilComponents(state,
    1607            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1608              :                                                         FirstHVACIteration,
    1609            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1610              :                                                         QCoilReq,
    1611              :                                                         fanOp,
    1612              :                                                         PartLoadRatio);
    1613            0 :                 break;
    1614              :             }
    1615            0 :             case HCoilType::SteamCoil: {
    1616            0 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1617            0 :                     mdot = 0.0;
    1618            0 :                     QCoilReq = 0.0;
    1619              :                 } else {
    1620            0 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1621            0 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1622            0 :                     QCoilReq =
    1623            0 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1624            0 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1625            0 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1626            0 :                     mdot = state.dataUnitHeaters->UnitHeat(UnitHeatNum).MaxHotSteamFlow * PartLoadRatio;
    1627              :                 }
    1628            0 :                 if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1629            0 :                 PlantUtilities::SetComponentFlowRate(state,
    1630              :                                                      mdot,
    1631            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotControlNode,
    1632            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HotCoilOutNodeNum,
    1633            0 :                                                      state.dataUnitHeaters->UnitHeat(UnitHeatNum).HWplantLoc);
    1634            0 :                 SteamCoils::SimulateSteamCoilComponents(state,
    1635            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1636              :                                                         FirstHVACIteration,
    1637            0 :                                                         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1638              :                                                         QCoilReq,
    1639              :                                                         _,
    1640              :                                                         fanOp,
    1641              :                                                         PartLoadRatio);
    1642            0 :                 break;
    1643              :             }
    1644          361 :             case HCoilType::Electric:
    1645              :             case HCoilType::Gas: {
    1646              : 
    1647          361 :                 if (!state.dataUnitHeaters->HCoilOn) {
    1648          358 :                     QCoilReq = 0.0;
    1649              :                 } else {
    1650            3 :                     HCoilInAirNode = state.dataUnitHeaters->UnitHeat(UnitHeatNum).FanOutletNode;
    1651            3 :                     CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).HumRat);
    1652            3 :                     QCoilReq =
    1653            3 :                         state.dataUnitHeaters->QZnReq - state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
    1654            3 :                                                             (state.dataLoopNodes->Node(HCoilInAirNode).Temp -
    1655            3 :                                                              state.dataLoopNodes->Node(state.dataUnitHeaters->UnitHeat(UnitHeatNum).AirInNode).Temp);
    1656              :                 }
    1657          361 :                 if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
    1658         1083 :                 HeatingCoils::SimulateHeatingCoilComponents(state,
    1659          361 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoilName,
    1660              :                                                             FirstHVACIteration,
    1661              :                                                             QCoilReq,
    1662          361 :                                                             state.dataUnitHeaters->UnitHeat(UnitHeatNum).HCoil_Index,
    1663              :                                                             _,
    1664              :                                                             _,
    1665              :                                                             fanOp,
    1666              :                                                             PartLoadRatio);
    1667          361 :                 break;
    1668              :             }
    1669            0 :             default:
    1670            0 :                 break;
    1671              :             }
    1672          361 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate =
    1673          361 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate; // maintain continuity through unit heater
    1674              :         }
    1675          377 :         LoadMet = AirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(OutletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat) -
    1676          377 :                                  Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(InletNode).Temp, state.dataLoopNodes->Node(InletNode).HumRat));
    1677          377 :     }
    1678              : 
    1679              :     // SUBROUTINE UpdateUnitHeater
    1680              : 
    1681              :     // No update routine needed in this module since all of the updates happen on
    1682              :     // the Node derived type directly and these updates are done by other routines.
    1683              : 
    1684              :     // END SUBROUTINE UpdateUnitHeater
    1685              : 
    1686          362 :     void ReportUnitHeater(EnergyPlusData &state, int const UnitHeatNum) // Unit index in unit heater array
    1687              :     {
    1688              : 
    1689              :         // SUBROUTINE INFORMATION:
    1690              :         //       AUTHOR         Rick Strand
    1691              :         //       DATE WRITTEN   May 2000
    1692              : 
    1693              :         // PURPOSE OF THIS SUBROUTINE:
    1694              :         // This subroutine needs a description.
    1695              : 
    1696              :         // METHODOLOGY EMPLOYED:
    1697              :         // Needs description, as appropriate.
    1698              : 
    1699              :         // Using/Aliasing
    1700          362 :         Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    1701              : 
    1702          362 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatEnergy = state.dataUnitHeaters->UnitHeat(UnitHeatNum).HeatPower * TimeStepSysSec;
    1703          362 :         state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecEnergy = state.dataUnitHeaters->UnitHeat(UnitHeatNum).ElecPower * TimeStepSysSec;
    1704              : 
    1705          362 :         if (state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass) { // reset sizing flags so other zone equipment can size normally
    1706            4 :             if (!state.dataGlobal->SysSizingCalc) {
    1707            1 :                 DataSizing::resetHVACSizingGlobals(state, state.dataSize->CurZoneEqNum, 0, state.dataUnitHeaters->UnitHeat(UnitHeatNum).FirstPass);
    1708              :             }
    1709              :         }
    1710          362 :     }
    1711              : 
    1712            1 :     int getUnitHeaterIndex(EnergyPlusData &state, std::string_view CompName)
    1713              :     {
    1714            1 :         if (state.dataUnitHeaters->GetUnitHeaterInputFlag) {
    1715            0 :             GetUnitHeaterInput(state);
    1716            0 :             state.dataUnitHeaters->GetUnitHeaterInputFlag = false;
    1717              :         }
    1718              : 
    1719            1 :         for (int UnitHeatNum = 1; UnitHeatNum <= state.dataUnitHeaters->NumOfUnitHeats; ++UnitHeatNum) {
    1720            1 :             if (Util::SameString(state.dataUnitHeaters->UnitHeat(UnitHeatNum).Name, CompName)) {
    1721            1 :                 return UnitHeatNum;
    1722              :             }
    1723              :         }
    1724              : 
    1725            0 :         return 0;
    1726              :     }
    1727              : 
    1728              : } // namespace UnitHeater
    1729              : 
    1730              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1