LCOV - code coverage report
Current view: top level - EnergyPlus - PoweredInductionUnits.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 71.8 % 1360 976
Test Date: 2025-05-22 16:09:37 Functions: 100.0 % 21 21

            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              : #include <ObjexxFCL/Fmath.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/Autosizing/Base.hh>
      57              : #include <EnergyPlus/BranchNodeConnections.hh>
      58              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59              : #include <EnergyPlus/DataDefineEquip.hh>
      60              : #include <EnergyPlus/DataEnvironment.hh>
      61              : #include <EnergyPlus/DataHeatBalFanSys.hh>
      62              : #include <EnergyPlus/DataIPShortCuts.hh>
      63              : #include <EnergyPlus/DataLoopNode.hh>
      64              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      65              : #include <EnergyPlus/DataSizing.hh>
      66              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      67              : #include <EnergyPlus/DataZoneEquipment.hh>
      68              : #include <EnergyPlus/Fans.hh>
      69              : #include <EnergyPlus/FluidProperties.hh>
      70              : #include <EnergyPlus/General.hh>
      71              : #include <EnergyPlus/GeneralRoutines.hh>
      72              : #include <EnergyPlus/GlobalNames.hh>
      73              : #include <EnergyPlus/HeatingCoils.hh>
      74              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      75              : #include <EnergyPlus/MixerComponent.hh>
      76              : #include <EnergyPlus/NodeInputManager.hh>
      77              : #include <EnergyPlus/OutputProcessor.hh>
      78              : #include <EnergyPlus/OutputReportPredefined.hh>
      79              : #include <EnergyPlus/Plant/DataPlant.hh>
      80              : #include <EnergyPlus/PlantUtilities.hh>
      81              : #include <EnergyPlus/PoweredInductionUnits.hh>
      82              : #include <EnergyPlus/Psychrometrics.hh>
      83              : #include <EnergyPlus/ScheduleManager.hh>
      84              : #include <EnergyPlus/SteamCoils.hh>
      85              : #include <EnergyPlus/UtilityRoutines.hh>
      86              : #include <EnergyPlus/WaterCoils.hh>
      87              : #include <EnergyPlus/ZoneAirLoopEquipmentManager.hh>
      88              : 
      89              : namespace EnergyPlus::PoweredInductionUnits {
      90              : 
      91              : // Module containing routines dealing with Series and Parallel fan powered terminal boxes
      92              : 
      93              : // MODULE INFORMATION:
      94              : //       AUTHOR         Fred Buhl
      95              : //       DATE WRITTEN   August 2000
      96              : //       MODIFIED       Brent Griffith, Sept 2010, plant upgrades, fluid properties
      97              : //       RE-ENGINEERED  na
      98              : 
      99              : // PURPOSE OF THIS MODULE:
     100              : // To encapsulate the data and algorithms needed to simulate Series and Parallel
     101              : // fan powered induction terminal boxes.
     102              : 
     103              : // METHODOLOGY EMPLOYED:
     104              : // The terminal boxes are modeled as a collection of components: air mixer,
     105              : // fan, and heating coil plus an integrated control
     106              : // algorithm that adjusts the primary air flow and the heating coil output
     107              : // to meet the zone load.
     108              : 
     109              : // Using/Aliasing
     110              : using namespace DataLoopNode;
     111              : using HVAC::SmallAirVolFlow;
     112              : using HVAC::SmallLoad;
     113              : using HVAC::SmallMassFlow;
     114              : using HVAC::SmallTempDiff;
     115              : using Psychrometrics::PsyCpAirFnW;
     116              : using Psychrometrics::PsyHFnTdbW;
     117              : using SteamCoils::SimulateSteamCoilComponents;
     118              : 
     119        15913 : void SimPIU(EnergyPlusData &state,
     120              :             std::string_view CompName,     // name of the PIU
     121              :             bool const FirstHVACIteration, // TRUE if first HVAC iteration in time step
     122              :             int const ZoneNum,             // index of zone served by PIU
     123              :             int const ZoneNodeNum,         // zone node number of zone served by PIU
     124              :             int &CompIndex                 // PIU Index in PIU names
     125              : )
     126              : {
     127              : 
     128              :     // SUBROUTINE INFORMATION:
     129              :     //       AUTHOR         Fred Buhl
     130              :     //       DATE WRITTEN   March 2000
     131              :     //       MODIFIED       na
     132              :     //       RE-ENGINEERED  na
     133              : 
     134              :     // PURPOSE OF THIS SUBROUTINE:
     135              :     // Manages the simulation of a fan powered induction terminal unit.
     136              :     // Called from SimZoneAirLoopEquipmentin module ZoneAirLoopEquipmentManager.
     137              : 
     138              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     139        15913 :     int PIUNum = 0; // index of powered induction unit being simulated
     140              : 
     141              :     // First time SimPIU is called, get the input for all the fan coil units
     142        15913 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
     143            6 :         GetPIUs(state);
     144            6 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
     145              :     }
     146              : 
     147              :     // Get the powered induction unit index
     148        15913 :     if (CompIndex == 0) {
     149            7 :         PIUNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU);
     150            7 :         if (PIUNum == 0) {
     151            0 :             ShowFatalError(state, format("SimPIU: PIU Unit not found={}", CompName));
     152              :         }
     153            7 :         CompIndex = PIUNum;
     154              :     } else {
     155        15906 :         PIUNum = CompIndex;
     156        15906 :         if (PIUNum > state.dataPowerInductionUnits->NumPIUs || PIUNum < 1) {
     157            0 :             ShowFatalError(state,
     158            0 :                            format("SimPIU: Invalid CompIndex passed={}, Number of PIU Units={}, PIU Unit name={}",
     159              :                                   CompIndex,
     160            0 :                                   state.dataPowerInductionUnits->NumPIUs,
     161              :                                   CompName));
     162              :         }
     163        15906 :         if (state.dataPowerInductionUnits->CheckEquipName(PIUNum)) {
     164            6 :             if (CompName != state.dataPowerInductionUnits->PIU(PIUNum).Name) {
     165            0 :                 ShowFatalError(state,
     166            0 :                                format("SimPIU: Invalid CompIndex passed={}, PIU Unit name={}, stored PIU Unit Name for that index={}",
     167              :                                       CompIndex,
     168              :                                       CompName,
     169            0 :                                       state.dataPowerInductionUnits->PIU(PIUNum).Name));
     170              :             }
     171            6 :             state.dataPowerInductionUnits->CheckEquipName(PIUNum) = false;
     172              :         }
     173              :     }
     174              : 
     175        31826 :     state.dataSize->CurTermUnitSizingNum =
     176        15913 :         state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(PIUNum).ADUNum).TermUnitSizingNum;
     177              :     // initialize the unit
     178        15913 :     InitPIU(state, PIUNum, FirstHVACIteration);
     179              : 
     180        15913 :     state.dataSize->TermUnitPIU = true;
     181              : 
     182              :     // Select the correct unit type
     183        15913 :     switch (state.dataPowerInductionUnits->PIU(PIUNum).UnitType_Num) {
     184              : 
     185        15913 :     case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat: { //  'AirTerminal:SingleDuct:SeriesPIU:Reheat'
     186              : 
     187        15913 :         CalcSeriesPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
     188        15913 :         break;
     189              :     }
     190            0 :     case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat: { // 'AirTerminal:SingleDuct:ParallelPIU:Reheat'
     191              : 
     192            0 :         CalcParallelPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
     193            0 :         break;
     194              :     }
     195            0 :     default:
     196            0 :         ShowSevereError(state, format("Illegal PI Unit Type used={}", state.dataPowerInductionUnits->PIU(PIUNum).UnitType));
     197            0 :         ShowContinueError(state, format("Occurs in PI Unit={}", state.dataPowerInductionUnits->PIU(PIUNum).Name));
     198            0 :         ShowFatalError(state, "Preceding condition causes termination.");
     199            0 :         break;
     200              :     }
     201              : 
     202        15913 :     state.dataSize->TermUnitPIU = false;
     203              : 
     204              :     // Update the current unit's outlet nodes
     205              :     // no update needed: reheat coil updates outlet node; inlet nodes' mass flow rate set by Calc routine
     206              : 
     207              :     // Fill the report variables
     208        15913 :     ReportPIU(state, PIUNum);
     209        15913 : }
     210              : 
     211           19 : void GetPIUs(EnergyPlusData &state)
     212              : {
     213              : 
     214              :     // SUBROUTINE INFORMATION:
     215              :     //       AUTHOR         Fred Buhl
     216              :     //       DATE WRITTEN   August 2000
     217              :     //       MODIFIED       na
     218              :     //       RE-ENGINEERED  na
     219              : 
     220              :     // PURPOSE OF THIS SUBROUTINE:
     221              :     // Obtains input data for powered induction unit terminal boxes and stores it
     222              :     // in PIU data structures
     223              : 
     224              :     // METHODOLOGY EMPLOYED:
     225              :     // Uses "Get" routines to read in data.
     226              : 
     227              :     // Using/Aliasing
     228              :     using BranchNodeConnections::SetUpCompSets;
     229              :     using BranchNodeConnections::TestCompSet;
     230              : 
     231              :     using NodeInputManager::GetOnlySingleNode;
     232              :     using SteamCoils::GetCoilSteamInletNode;
     233              :     using WaterCoils::GetCoilWaterInletNode;
     234              : 
     235              :     static constexpr std::string_view routineName = "GetPIUs";
     236              : 
     237              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     238           19 :     bool ErrorsFound(false);                                    // Set to true if errors in input, fatal at end of routine
     239              :     static constexpr std::string_view RoutineName("GetPIUs: "); // include trailing blank space
     240           19 :     bool SteamMessageNeeded = true;
     241              : 
     242              :     // find the number of each type of fan coil unit
     243           38 :     state.dataPowerInductionUnits->NumSeriesPIUs =
     244           19 :         state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:SeriesPIU:Reheat");
     245           38 :     state.dataPowerInductionUnits->NumParallelPIUs =
     246           19 :         state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat");
     247           19 :     state.dataPowerInductionUnits->NumPIUs = state.dataPowerInductionUnits->NumSeriesPIUs + state.dataPowerInductionUnits->NumParallelPIUs;
     248              : 
     249           19 :     if (state.dataPowerInductionUnits->NumPIUs > 0) {
     250              :         // GetZonePlenumInput might call this routine before the AirDistUnit has been populated
     251           17 :         if (state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag) {
     252            3 :             ZoneAirLoopEquipmentManager::GetZoneAirLoopEquipment(state);
     253            3 :             state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag = false;
     254              :         }
     255              :     }
     256              : 
     257              :     // allocate the data structures
     258           19 :     state.dataPowerInductionUnits->PIU.allocate(state.dataPowerInductionUnits->NumPIUs);
     259           19 :     state.dataPowerInductionUnits->PiuUniqueNames.reserve(static_cast<unsigned>(state.dataPowerInductionUnits->NumPIUs));
     260           19 :     state.dataPowerInductionUnits->CheckEquipName.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     261              : 
     262           19 :     int PIUNum = 0;
     263           19 :     auto &ip = state.dataInputProcessing->inputProcessor;
     264              :     // loop over Series PIUs; get and load the input data
     265           95 :     for (const std::string cCurrentModuleObject : {"AirTerminal:SingleDuct:SeriesPIU:Reheat", "AirTerminal:SingleDuct:ParallelPIU:Reheat"}) {
     266           38 :         auto const &objectSchemaProps = ip->getObjectSchemaProps(state, cCurrentModuleObject);
     267           38 :         auto const &PIUsInstances = ip->epJSON.find(cCurrentModuleObject);
     268           38 :         if (PIUsInstances != ip->epJSON.end()) {
     269           17 :             auto &PIUInstances = PIUsInstances.value();
     270           34 :             for (auto instance = PIUInstances.begin(); instance != PIUInstances.end(); ++instance) {
     271           17 :                 ++PIUNum;
     272           17 :                 auto const &fields = instance.value();
     273              : 
     274           17 :                 ErrorObjectHeader eoh{routineName, cCurrentModuleObject, instance.key()};
     275              : 
     276           17 :                 GlobalNames::VerifyUniqueInterObjectName(
     277           17 :                     state, state.dataPowerInductionUnits->PiuUniqueNames, Util::makeUPPER(instance.key()), cCurrentModuleObject, "Name", ErrorsFound);
     278           17 :                 auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
     279           17 :                 thisPIU.Name = Util::makeUPPER(instance.key());
     280           17 :                 thisPIU.UnitType = cCurrentModuleObject;
     281           17 :                 ip->markObjectAsUsed(cCurrentModuleObject, instance.key());
     282           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     283           14 :                     thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat;
     284            3 :                 } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     285            3 :                     thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat;
     286              :                 }
     287              : 
     288           34 :                 std::string schedName = ip->getAlphaFieldValue(fields, objectSchemaProps, "availability_schedule_name");
     289           17 :                 if (schedName.empty()) {
     290            3 :                     thisPIU.availSched = Sched::GetScheduleAlwaysOn(state);
     291           14 :                 } else if ((thisPIU.availSched = Sched::GetSchedule(state, Util::makeUPPER(schedName))) == nullptr) {
     292            0 :                     ShowWarningItemNotFound(
     293              :                         state, eoh, "Availability Schedule Name", schedName, "Set the default as Always On. Simulation continues.");
     294            0 :                     thisPIU.availSched = Sched::GetScheduleAlwaysOn(state);
     295              :                 }
     296              : 
     297           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     298           42 :                     thisPIU.MaxTotAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_air_flow_rate");
     299              :                 }
     300           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     301            9 :                     thisPIU.MaxSecAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_secondary_air_flow_rate");
     302              :                 }
     303           34 :                 thisPIU.MaxPriAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_primary_air_flow_rate");
     304           34 :                 thisPIU.MinPriAirFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_primary_air_flow_fraction");
     305           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     306            9 :                     thisPIU.FanOnFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "fan_on_flow_fraction");
     307              :                 }
     308           17 :                 thisPIU.HCoilType = static_cast<HtgCoilType>(
     309           34 :                     getEnumValue(HCoilNamesUC, Util::makeUPPER(ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"))));
     310           17 :                 switch (thisPIU.HCoilType) {
     311            1 :                 case HtgCoilType::SimpleHeating: {
     312            1 :                     thisPIU.HCoil_PlantType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
     313            1 :                     break;
     314              :                 }
     315           16 :                 case HtgCoilType::Electric:
     316              :                 case HtgCoilType::Gas: {
     317           16 :                     break;
     318              :                 }
     319            0 :                 case HtgCoilType::SteamAirHeating: {
     320            0 :                     thisPIU.HCoil_PlantType = DataPlant::PlantEquipmentType::CoilSteamAirHeating;
     321            0 :                     thisPIU.HCoil_fluid = Fluid::GetSteam(state);
     322            0 :                     if (thisPIU.HCoil_fluid == nullptr) {
     323            0 :                         ShowSevereError(state, format("{} Steam Properties for {} not found.", RoutineName, thisPIU.Name));
     324            0 :                         if (SteamMessageNeeded) {
     325            0 :                             ShowContinueError(state, "Steam Fluid Properties should have been included in the input file.");
     326              :                         }
     327            0 :                         ErrorsFound = true;
     328            0 :                         SteamMessageNeeded = false;
     329              :                     }
     330            0 :                     break;
     331              :                 }
     332            0 :                 default: {
     333            0 :                     ShowSevereError(state, format("Illegal Reheat Coil Type = {}", thisPIU.HCoilType));
     334            0 :                     ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
     335            0 :                     ErrorsFound = true;
     336              :                 }
     337              :                 }
     338              : 
     339           17 :                 auto connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctSeriesPIUReheat;
     340           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     341            3 :                     connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctParallelPIUReheat;
     342              :                 }
     343           34 :                 thisPIU.PriAirInNode = GetOnlySingleNode(state,
     344           34 :                                                          ip->getAlphaFieldValue(fields, objectSchemaProps, "supply_air_inlet_node_name"),
     345              :                                                          ErrorsFound,
     346              :                                                          connectionType,
     347           17 :                                                          thisPIU.Name,
     348              :                                                          DataLoopNode::NodeFluidType::Air,
     349              :                                                          DataLoopNode::ConnectionType::Inlet,
     350              :                                                          NodeInputManager::CompFluidStream::Primary,
     351              :                                                          ObjectIsParent,
     352              :                                                          "Supply Air Inlet Node Name");
     353              : 
     354           34 :                 thisPIU.SecAirInNode = GetOnlySingleNode(state,
     355           34 :                                                          ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
     356              :                                                          ErrorsFound,
     357              :                                                          connectionType,
     358           17 :                                                          thisPIU.Name,
     359              :                                                          DataLoopNode::NodeFluidType::Air,
     360              :                                                          DataLoopNode::ConnectionType::Inlet,
     361              :                                                          NodeInputManager::CompFluidStream::Primary,
     362              :                                                          ObjectIsParent,
     363              :                                                          "Secondary Air Inlet Node Name");
     364              : 
     365           34 :                 thisPIU.OutAirNode = GetOnlySingleNode(state,
     366           34 :                                                        ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"),
     367              :                                                        ErrorsFound,
     368              :                                                        connectionType,
     369           17 :                                                        thisPIU.Name,
     370              :                                                        DataLoopNode::NodeFluidType::Air,
     371              :                                                        DataLoopNode::ConnectionType::Outlet,
     372              :                                                        NodeInputManager::CompFluidStream::Primary,
     373              :                                                        ObjectIsParent,
     374              :                                                        "Outlet Node Name");
     375              : 
     376           34 :                 thisPIU.HCoilInAirNode = GetOnlySingleNode(state,
     377           34 :                                                            ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
     378              :                                                            ErrorsFound,
     379              :                                                            connectionType,
     380           17 :                                                            thisPIU.Name,
     381              :                                                            DataLoopNode::NodeFluidType::Air,
     382              :                                                            DataLoopNode::ConnectionType::Internal,
     383              :                                                            NodeInputManager::CompFluidStream::Primary,
     384              :                                                            ObjectIsParent,
     385              :                                                            "Reheat Coil Air Inlet Node Name");
     386              :                 // The reheat coil control node is necessary for hot water reheat, but not necessary for
     387              :                 // electric or gas reheat.
     388           17 :                 if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
     389            1 :                     thisPIU.HotControlNode = GetCoilWaterInletNode(state,
     390            3 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
     391            4 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
     392              :                                                                    ErrorsFound);
     393              :                 }
     394           17 :                 if (thisPIU.HCoilType == HtgCoilType::SteamAirHeating) {
     395            0 :                     thisPIU.HotControlNode = GetCoilSteamInletNode(state,
     396            0 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
     397            0 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
     398              :                                                                    ErrorsFound);
     399              :                 }
     400           34 :                 thisPIU.MixerName = ip->getAlphaFieldValue(fields, objectSchemaProps, "zone_mixer_name");
     401           34 :                 thisPIU.FanName = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_name");
     402              : 
     403              :                 // find fan type
     404              :                 // test if Fan:SystemModel fan of this name exists
     405           17 :                 if ((thisPIU.Fan_Index = Fans::GetFanIndex(state, thisPIU.FanName)) == 0) {
     406            0 :                     ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(8), thisPIU.FanName);
     407            0 :                     ErrorsFound = true;
     408              :                 } else {
     409              :                     // Assert that this is a constant volume fan?
     410           17 :                     auto *fan = state.dataFans->fans(thisPIU.Fan_Index);
     411           17 :                     thisPIU.fanType = fan->type;
     412           17 :                     thisPIU.fanAvailSched = fan->availSched;
     413              :                 }
     414              : 
     415           34 :                 thisPIU.HCoil = ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name");
     416           17 :                 bool IsNotOK = false;
     417           34 :                 ValidateComponent(
     418           51 :                     state, HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], thisPIU.HCoil, IsNotOK, cCurrentModuleObject + " - Heating Coil");
     419           17 :                 if (IsNotOK) {
     420            0 :                     ShowContinueError(state, format("In {} = {}", cCurrentModuleObject, thisPIU.Name));
     421            0 :                     ErrorsFound = true;
     422              :                 }
     423           34 :                 thisPIU.MaxVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_hot_water_or_steam_flow_rate");
     424           34 :                 thisPIU.MinVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_hot_water_or_steam_flow_rate");
     425           34 :                 thisPIU.HotControlOffset = ip->getRealFieldValue(fields, objectSchemaProps, "convergence_tolerance");
     426              :                 // Set default convergence tolerance
     427           17 :                 if (thisPIU.HotControlOffset <= 0.0) {
     428            0 :                     thisPIU.HotControlOffset = 0.001;
     429              :                 }
     430              : 
     431              :                 // Variable speed fan inputs
     432           34 :                 std::string fan_control_type = "ConstantSpeed";
     433           34 :                 fan_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_control_type");
     434           17 :                 thisPIU.fanControlType = FanCntrlType::ConstantSpeedFan;
     435           17 :                 if (Util::SameString(fan_control_type, "VariableSpeed")) {
     436            5 :                     thisPIU.fanControlType = FanCntrlType::VariableSpeedFan;
     437            5 :                     if (thisPIU.fanType != HVAC::FanType::SystemModel) {
     438            0 :                         ErrorsFound = true;
     439            0 :                         ShowSevereError(state, format("Fan type must be Fan:SystemModel when Fan Control Type = {}", fan_control_type));
     440            0 :                         ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
     441              :                     }
     442           12 :                 } else if (Util::SameString(fan_control_type, "ConstantSpeed")) {
     443           12 :                     thisPIU.fanControlType = FanCntrlType::ConstantSpeedFan;
     444              :                 } else {
     445            0 :                     ShowSevereError(state, format("Illegal Fan Control Type = {}", fan_control_type));
     446            0 :                     ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
     447            0 :                     ErrorsFound = true;
     448              :                 }
     449              : 
     450           51 :                 std::string const heating_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "heating_control_type");
     451           17 :                 thisPIU.heatingControlType = HeatCntrlBehaviorType::Invalid;
     452           17 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
     453            5 :                     if (Util::SameString(heating_control_type, "Staged")) {
     454            2 :                         thisPIU.heatingControlType = HeatCntrlBehaviorType::StagedHeaterBehavior;
     455            3 :                     } else if (Util::SameString(heating_control_type, "Modulated")) {
     456            3 :                         thisPIU.heatingControlType = HeatCntrlBehaviorType::ModulatedHeaterBehavior;
     457              :                     } else {
     458            0 :                         ShowSevereError(state, "Heating Control Type should either be Staged or Modulated");
     459            0 :                         ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
     460            0 :                         ErrorsFound = true;
     461              :                     }
     462              :                 }
     463              : 
     464           34 :                 thisPIU.MinFanTurnDownRatio = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_fan_turn_down_ratio");
     465           34 :                 thisPIU.designHeatingDAT = ip->getRealFieldValue(fields, objectSchemaProps, "design_heating_discharge_air_temperature");
     466           34 :                 thisPIU.highLimitDAT = ip->getRealFieldValue(fields, objectSchemaProps, "high_limit_heating_discharge_air_temperature");
     467              : 
     468              :                 // Add fan to component sets array
     469           17 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     470           28 :                     SetUpCompSets(state,
     471              :                                   thisPIU.UnitType,
     472              :                                   thisPIU.Name,
     473              :                                   "UNDEFINED",
     474              :                                   thisPIU.FanName,
     475              :                                   "UNDEFINED",
     476           56 :                                   ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"));
     477            3 :                 } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     478            6 :                     SetUpCompSets(state,
     479              :                                   thisPIU.UnitType,
     480              :                                   thisPIU.Name,
     481              :                                   "UNDEFINED",
     482              :                                   thisPIU.FanName,
     483           12 :                                   ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
     484              :                                   "UNDEFINED");
     485              :                 }
     486              : 
     487              :                 // Add reheat coil to component sets array
     488           85 :                 SetUpCompSets(state,
     489              :                               thisPIU.UnitType,
     490              :                               thisPIU.Name,
     491           51 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
     492           51 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
     493           51 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
     494           68 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"));
     495              : 
     496              :                 // Register component set data
     497           34 :                 TestCompSet(state,
     498              :                             thisPIU.UnitType,
     499              :                             thisPIU.Name,
     500           17 :                             state.dataLoopNodes->NodeID(thisPIU.PriAirInNode),
     501           17 :                             state.dataLoopNodes->NodeID(thisPIU.OutAirNode),
     502              :                             "Air Nodes");
     503              : 
     504           39 :                 for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     505           22 :                     if (thisPIU.OutAirNode == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
     506           17 :                         thisPIU.ADUNum = ADUNum;
     507           17 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisPIU.PriAirInNode;
     508              :                     }
     509              :                 }
     510              :                 // one assumes if there isn't one assigned, it's an error?
     511           17 :                 if (thisPIU.ADUNum == 0) {
     512            0 :                     ShowSevereError(state,
     513            0 :                                     format("{}No matching Air Distribution Unit, for PIU = [{},{}].", RoutineName, thisPIU.UnitType, thisPIU.Name));
     514            0 :                     ShowContinueError(state, format("...should have outlet node = {}", state.dataLoopNodes->NodeID(thisPIU.OutAirNode)));
     515            0 :                     ErrorsFound = true;
     516              :                 } else {
     517              : 
     518           17 :                     bool AirNodeFound = false;
     519              :                     // Fill the Zone Equipment data with the supply air inlet node number of this unit.
     520           41 :                     for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     521           24 :                         if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     522            2 :                             continue;
     523              :                         }
     524           27 :                         for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++SupAirIn) {
     525           22 :                             if (thisPIU.OutAirNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(SupAirIn)) {
     526           17 :                                 state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).InNode = thisPIU.PriAirInNode;
     527           17 :                                 state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).OutNode = thisPIU.OutAirNode;
     528           17 :                                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).TermUnitSizingNum =
     529           17 :                                     state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
     530           17 :                                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).ZoneEqNum = CtrlZone;
     531           17 :                                 AirNodeFound = true;
     532           17 :                                 thisPIU.CtrlZoneNum = CtrlZone; // fill index for later use in finding air loop index
     533           17 :                                 thisPIU.ctrlZoneInNodeIndex = SupAirIn;
     534           17 :                                 break;
     535              :                             }
     536              :                         }
     537              :                     }
     538           17 :                     if (!AirNodeFound) {
     539            0 :                         ShowSevereError(state, format("The outlet air node from the {} Unit = {}", cCurrentModuleObject, thisPIU.Name));
     540            0 :                         ShowContinueError(
     541            0 :                             state, format("did not have a matching Zone Equipment Inlet Node, Node = {}", state.dataIPShortCut->cAlphaArgs(5)));
     542            0 :                         ErrorsFound = true;
     543              :                     }
     544              :                 }
     545           17 :             }
     546              :         }
     547           38 :     }
     548              : 
     549           19 :     if (ErrorsFound) {
     550            0 :         ShowFatalError(state, format("{} Errors found in getting input.  Preceding conditions cause termination.", RoutineName));
     551              :     }
     552              : 
     553           36 :     for (int PIURpt = 1; PIURpt <= state.dataPowerInductionUnits->NumPIUs; ++PIURpt) {
     554           17 :         auto &thisPIU = state.dataPowerInductionUnits->PIU(PIURpt);
     555              : 
     556              :         // Setup Report variables for the PIUs
     557           34 :         SetupOutputVariable(state,
     558              :                             "Zone Air Terminal Primary Damper Position",
     559              :                             Constant::Units::None,
     560           17 :                             thisPIU.PriDamperPosition,
     561              :                             OutputProcessor::TimeStepType::System,
     562              :                             OutputProcessor::StoreType::Average,
     563           17 :                             thisPIU.Name);
     564           34 :         SetupOutputVariable(state,
     565              :                             "Zone Air Terminal Heating Rate",
     566              :                             Constant::Units::W,
     567           17 :                             thisPIU.HeatingRate,
     568              :                             OutputProcessor::TimeStepType::System,
     569              :                             OutputProcessor::StoreType::Average,
     570           17 :                             thisPIU.Name);
     571           34 :         SetupOutputVariable(state,
     572              :                             "Zone Air Terminal Heating Energy",
     573              :                             Constant::Units::J,
     574           17 :                             thisPIU.HeatingEnergy,
     575              :                             OutputProcessor::TimeStepType::System,
     576              :                             OutputProcessor::StoreType::Sum,
     577           17 :                             thisPIU.Name);
     578           34 :         SetupOutputVariable(state,
     579              :                             "Zone Air Terminal Sensible Cooling Rate",
     580              :                             Constant::Units::W,
     581           17 :                             thisPIU.SensCoolRate,
     582              :                             OutputProcessor::TimeStepType::System,
     583              :                             OutputProcessor::StoreType::Average,
     584           17 :                             thisPIU.Name);
     585           34 :         SetupOutputVariable(state,
     586              :                             "Zone Air Terminal Sensible Cooling Energy",
     587              :                             Constant::Units::J,
     588           17 :                             thisPIU.SensCoolEnergy,
     589              :                             OutputProcessor::TimeStepType::System,
     590              :                             OutputProcessor::StoreType::Sum,
     591           17 :                             thisPIU.Name);
     592           34 :         SetupOutputVariable(state,
     593              :                             "Zone Air Terminal Outdoor Air Volume Flow Rate",
     594              :                             Constant::Units::m3_s,
     595           17 :                             thisPIU.OutdoorAirFlowRate,
     596              :                             OutputProcessor::TimeStepType::System,
     597              :                             OutputProcessor::StoreType::Average,
     598           17 :                             thisPIU.Name);
     599           34 :         SetupOutputVariable(state,
     600              :                             "Zone Air Terminal Total Air Mass Flow Rate",
     601              :                             Constant::Units::kg_s,
     602           17 :                             thisPIU.TotMassFlowRate,
     603              :                             OutputProcessor::TimeStepType::System,
     604              :                             OutputProcessor::StoreType::Average,
     605           17 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     606           34 :         SetupOutputVariable(state,
     607              :                             "Zone Air Terminal Primary Air Mass Flow Rate",
     608              :                             Constant::Units::kg_s,
     609           17 :                             thisPIU.PriMassFlowRate,
     610              :                             OutputProcessor::TimeStepType::System,
     611              :                             OutputProcessor::StoreType::Average,
     612           17 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     613           34 :         SetupOutputVariable(state,
     614              :                             "Zone Air Terminal Secondary Air Mass Flow Rate",
     615              :                             Constant::Units::kg_s,
     616           17 :                             thisPIU.SecMassFlowRate,
     617              :                             OutputProcessor::TimeStepType::System,
     618              :                             OutputProcessor::StoreType::Average,
     619           17 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     620           34 :         SetupOutputVariable(state,
     621              :                             "Zone Air Terminal Outlet Discharge Air Temperature",
     622              :                             Constant::Units::C,
     623           17 :                             thisPIU.DischargeAirTemp,
     624              :                             OutputProcessor::TimeStepType::System,
     625              :                             OutputProcessor::StoreType::Average,
     626           17 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     627           17 :         SetupOutputVariable(state,
     628              :                             "Zone Air Terminal Current Operation Control Stage",
     629              :                             Constant::Units::unknown,
     630           17 :                             thisPIU.CurOperationControlStage,
     631              :                             OutputProcessor::TimeStepType::System,
     632              :                             OutputProcessor::StoreType::Average,
     633           17 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     634              :     }
     635           19 : }
     636              : 
     637        15921 : void InitPIU(EnergyPlusData &state,
     638              :              int const PIUNum,             // number of the current fan coil unit being simulated
     639              :              bool const FirstHVACIteration // TRUE if first zone equip this HVAC step
     640              : )
     641              : {
     642              : 
     643              :     // SUBROUTINE INFORMATION:
     644              :     //       AUTHOR         Fred Buhl
     645              :     //       DATE WRITTEN   August 2000
     646              :     //       MODIFIED       na
     647              :     //       RE-ENGINEERED  na
     648              : 
     649              :     // PURPOSE OF THIS SUBROUTINE:
     650              :     // This subroutine is for initializations of the powered induction unit
     651              :     // terminal boxe.
     652              : 
     653              :     // METHODOLOGY EMPLOYED:
     654              :     // Uses the status flags to trigger initializations.
     655              : 
     656              :     // Using/Aliasing
     657              : 
     658              :     using DataZoneEquipment::CheckZoneEquipmentList;
     659              :     using PlantUtilities::InitComponentNodes;
     660              :     using PlantUtilities::ScanPlantLoopsForObject;
     661              : 
     662        15921 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
     663              :     // SUBROUTINE PARAMETER DEFINITIONS:
     664              :     static constexpr std::string_view RoutineName("InitPIU");
     665              : 
     666              :     // Do the one time initializations
     667        15921 :     if (state.dataPowerInductionUnits->MyOneTimeFlag) {
     668           15 :         state.dataPowerInductionUnits->MyEnvrnFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     669           15 :         state.dataPowerInductionUnits->MySizeFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     670           15 :         state.dataPowerInductionUnits->MyPlantScanFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     671           15 :         state.dataPowerInductionUnits->MyOneTimeFlag = false;
     672              :     }
     673              : 
     674        15921 :     if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && allocated(state.dataPlnt->PlantLoop)) {
     675            7 :         if ((thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
     676            7 :             (thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
     677            0 :             bool errFlag = false;
     678            0 :             ScanPlantLoopsForObject(state, thisPIU.HCoil, thisPIU.HCoil_PlantType, thisPIU.HWplantLoc, errFlag, _, _, _, _, _);
     679            0 :             if (errFlag) {
     680            0 :                 ShowFatalError(state, "InitPIU: Program terminated due to previous condition(s).");
     681              :             }
     682            0 :             thisPIU.HotCoilOutNodeNum = DataPlant::CompData::getPlantComponent(state, thisPIU.HWplantLoc).NodeNumOut;
     683              :         }
     684            7 :         state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
     685        15914 :     } else if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && !state.dataGlobal->AnyPlantInModel) {
     686            8 :         state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
     687              :     }
     688              : 
     689        15921 :     if (!state.dataPowerInductionUnits->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
     690            7 :         state.dataPowerInductionUnits->ZoneEquipmentListChecked = true;
     691              :         // Check to see if there is a Air Distribution Unit on the Zone Equipment List
     692           14 :         for (int Loop = 1; Loop <= state.dataPowerInductionUnits->NumPIUs; ++Loop) {
     693            7 :             if (state.dataPowerInductionUnits->PIU(Loop).ADUNum == 0) {
     694            0 :                 continue;
     695              :             }
     696           14 :             if (CheckZoneEquipmentList(state,
     697              :                                        "ZoneHVAC:AirDistributionUnit",
     698            7 :                                        state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(Loop).ADUNum).Name)) {
     699            7 :                 continue;
     700              :             }
     701            0 :             ShowSevereError(state,
     702            0 :                             format("InitPIU: ADU=[Air Distribution Unit,{}] is not on any ZoneHVAC:EquipmentList.",
     703            0 :                                    state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(Loop).ADUNum).Name));
     704            0 :             ShowContinueError(state,
     705            0 :                               format("...PIU=[{},{}] will not be simulated.",
     706            0 :                                      state.dataPowerInductionUnits->PIU(Loop).UnitType,
     707            0 :                                      state.dataPowerInductionUnits->PIU(Loop).Name));
     708              :         }
     709              :     }
     710              : 
     711        15935 :     if (!state.dataGlobal->SysSizingCalc && state.dataPowerInductionUnits->MySizeFlag(PIUNum) &&
     712           14 :         !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
     713              : 
     714           14 :         SizePIU(state, PIUNum);
     715              : 
     716              :         // If there's a hot water control node number defined in PIU
     717           14 :         if (thisPIU.HotControlNode > 0) {
     718              :             // plant upgrade note? why no separate handling of steam coil? add it ?
     719              :             // local plant fluid density
     720            0 :             Real64 const rho = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     721              : 
     722            0 :             thisPIU.MaxHotWaterFlow = rho * thisPIU.MaxVolHotWaterFlow;
     723            0 :             thisPIU.MinHotWaterFlow = rho * thisPIU.MinVolHotWaterFlow;
     724            0 :             InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
     725              :         }
     726              : 
     727           14 :         state.dataPowerInductionUnits->MySizeFlag(PIUNum) = false;
     728              :     }
     729              : 
     730        15921 :     int const PriNode = thisPIU.PriAirInNode;
     731        15921 :     int const SecNode = thisPIU.SecAirInNode;
     732              : 
     733              :     // Do the Begin Environment initializations
     734        15921 :     if (state.dataGlobal->BeginEnvrnFlag && state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum)) {
     735           32 :         Real64 const RhoAir = state.dataEnvrn->StdRhoAir;
     736           32 :         int const OutletNode = thisPIU.OutAirNode;
     737              :         // set the mass flow rates from the input volume flow rates
     738           32 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     739           29 :             thisPIU.MaxTotAirMassFlow = RhoAir * thisPIU.MaxTotAirVolFlow;
     740           29 :             thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
     741           29 :             thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
     742           29 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     743           29 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
     744           29 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
     745              :         } else {
     746              :             // parallel
     747            3 :             thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
     748            3 :             thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
     749            3 :             thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     750            3 :             thisPIU.FanOnAirMassFlow = RhoAir * thisPIU.FanOnFlowFrac * thisPIU.MaxPriAirVolFlow;
     751            3 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     752            3 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
     753            3 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     754              :         }
     755           32 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
     756            5 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     757            3 :                 thisPIU.MinTotAirMassFlow = thisPIU.MaxTotAirMassFlow * thisPIU.MinFanTurnDownRatio;
     758            3 :                 thisPIU.MaxSecAirVolFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
     759            3 :                 thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     760            3 :                 thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
     761              :             } else {
     762            2 :                 thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     763            2 :                 thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MaxSecAirMassFlow * thisPIU.MinFanTurnDownRatio);
     764            2 :                 thisPIU.MinTotAirMassFlow = thisPIU.MinSecAirMassFlow + thisPIU.MinPriAirMassFlow;
     765              :             }
     766              :         }
     767              : 
     768           32 :         if (((thisPIU.HCoilType == HtgCoilType::SimpleHeating) || (thisPIU.HCoilType == HtgCoilType::SteamAirHeating)) &&
     769            0 :             !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
     770            0 :             InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
     771              :         }
     772              : 
     773           32 :         if (thisPIU.AirLoopNum == 0) { // fill air loop index
     774           14 :             if (thisPIU.CtrlZoneNum > 0 && thisPIU.ctrlZoneInNodeIndex > 0) {
     775           14 :                 thisPIU.AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
     776           14 :                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).AirLoopNum = thisPIU.AirLoopNum;
     777              :             }
     778              :         }
     779              : 
     780           32 :         state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = false;
     781              :     } // end one time inits
     782              : 
     783        15921 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     784        15711 :         state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = true;
     785              :     }
     786              : 
     787              :     // Do the start of HVAC time step initializations
     788        15921 :     if (FirstHVACIteration) {
     789              :         // check for upstream zero flow. If nonzero and schedule ON, set primary flow to max
     790         7983 :         if (thisPIU.availSched->getCurrentVal() > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRate > 0.0) {
     791         7944 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     792         7944 :                 state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
     793         7944 :                 state.dataLoopNodes->Node(SecNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
     794              :             } else {
     795            0 :                 state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
     796            0 :                 state.dataLoopNodes->Node(SecNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
     797              :             }
     798              :         } else {
     799           39 :             state.dataLoopNodes->Node(PriNode).MassFlowRate = 0.0;
     800           39 :             state.dataLoopNodes->Node(SecNode).MassFlowRate = 0.0;
     801              :         }
     802              :         // reset the max and min avail flows
     803         7983 :         if (thisPIU.availSched->getCurrentVal() > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail > 0.0) {
     804         7944 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     805         7944 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
     806         7944 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
     807         7944 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
     808         7944 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
     809              :             } else {
     810            0 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
     811            0 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
     812            0 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
     813            0 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
     814              :             }
     815              :         } else {
     816           39 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = 0.0;
     817           39 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = 0.0;
     818           39 :             state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = 0.0;
     819           39 :             state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
     820              :         }
     821              :     }
     822              : 
     823              :     // Do the following initializations every time step
     824              : 
     825              :     // None needed
     826        15921 : }
     827              : 
     828           15 : void SizePIU(EnergyPlusData &state, int const PIUNum)
     829              : {
     830              : 
     831              :     // SUBROUTINE INFORMATION:
     832              :     //       AUTHOR         Fred Buhl
     833              :     //       DATE WRITTEN   January 2002
     834              :     //       MODIFIED       August 2013 Daeho Kang, add component sizing table entries
     835              :     //       RE-ENGINEERED  na
     836              : 
     837              :     // PURPOSE OF THIS SUBROUTINE:
     838              :     // This subroutine is for sizing PIU terminal units for which flow rates have not been
     839              :     // specified in the input.
     840              : 
     841              :     // METHODOLOGY EMPLOYED:
     842              :     // Obtains flow rates from the zone or system sizing arrays.
     843              : 
     844              :     // Using/Aliasing
     845              :     using namespace DataSizing;
     846              :     using SteamCoils::GetCoilSteamInletNode;
     847              :     using SteamCoils::GetCoilSteamOutletNode;
     848              :     using WaterCoils::GetCoilWaterInletNode;
     849              :     using WaterCoils::GetCoilWaterOutletNode;
     850              :     using WaterCoils::SetCoilDesFlow;
     851              : 
     852              :     using PlantUtilities::MyPlantSizingIndex;
     853              : 
     854              :     // SUBROUTINE PARAMETER DEFINITIONS:
     855              :     static constexpr std::string_view RoutineName("SizePIU");
     856              : 
     857              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     858           15 :     bool IsAutoSize = false;           // Indicator to autosize
     859           15 :     bool IsMaxPriFlowAutoSize = false; // Indicate if the maximum terminal flow is autosize
     860           15 :     int SysSizNum = 0;                 // System sizing number
     861           15 :     Real64 DesCoilLoad = 0.0;
     862           15 :     bool ErrorsFound = false;
     863              : 
     864           15 :     auto &TermUnitSizing(state.dataSize->TermUnitSizing);
     865           15 :     auto &CurTermUnitSizingNum(state.dataSize->CurTermUnitSizingNum);
     866           15 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
     867              : 
     868           15 :     if (thisPIU.MaxPriAirVolFlow == AutoSize) {
     869            6 :         IsAutoSize = true;
     870              :     }
     871           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     872            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
     873            0 :             if (thisPIU.MaxPriAirVolFlow > 0.0) {
     874            0 :                 BaseSizer::reportSizerOutput(
     875              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Primary Air Flow Rate [m3/s]", thisPIU.MaxPriAirVolFlow);
     876              :             }
     877              :         } else {
     878            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     879              :             // Autosized maximum primary air flow for reporting
     880            7 :             Real64 MaxPriAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     881            7 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     882            7 :             if (MaxPriAirVolFlowDes < SmallAirVolFlow) {
     883            0 :                 MaxPriAirVolFlowDes = 0.0;
     884              :             }
     885              : 
     886            7 :             if (IsAutoSize) {
     887            6 :                 thisPIU.MaxPriAirVolFlow = MaxPriAirVolFlowDes;
     888            6 :                 IsMaxPriFlowAutoSize = true;
     889            6 :                 BaseSizer::reportSizerOutput(
     890              :                     state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Primary Air Flow Rate [m3/s]", MaxPriAirVolFlowDes);
     891              :             } else {
     892            1 :                 if (thisPIU.MaxPriAirVolFlow > 0.0 && MaxPriAirVolFlowDes > 0.0) {
     893              :                     // Hardsized maximum primary air flow for reporting
     894            1 :                     Real64 const MaxPriAirVolFlowUser = thisPIU.MaxPriAirVolFlow;
     895            1 :                     BaseSizer::reportSizerOutput(state,
     896              :                                                  thisPIU.UnitType,
     897              :                                                  thisPIU.Name,
     898              :                                                  "Design Size Maximum Primary Air Flow Rate [m3/s]",
     899              :                                                  MaxPriAirVolFlowDes,
     900              :                                                  "User-Specified Maximum Primary Air Flow Rate [m3/s]",
     901              :                                                  MaxPriAirVolFlowUser);
     902            1 :                     if (state.dataGlobal->DisplayExtraWarnings) {
     903            1 :                         if ((std::abs(MaxPriAirVolFlowDes - MaxPriAirVolFlowUser) / MaxPriAirVolFlowUser) >
     904            1 :                             state.dataSize->AutoVsHardSizingThreshold) {
     905            1 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
     906            1 :                             ShowContinueError(state, format("User-Specified Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowUser));
     907            1 :                             ShowContinueError(state, format("differs from Design Size Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowDes));
     908            2 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     909            3 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     910              :                         }
     911              :                     }
     912              :                 }
     913              :             }
     914              :         }
     915              :     }
     916              : 
     917           15 :     IsAutoSize = false;
     918           15 :     if (thisPIU.MaxTotAirVolFlow == AutoSize) {
     919            7 :         IsAutoSize = true;
     920              :     }
     921           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     922            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
     923            0 :             if (thisPIU.MaxTotAirVolFlow > 0.0) {
     924            0 :                 BaseSizer::reportSizerOutput(
     925              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Air Flow Rate [m3/s]", thisPIU.MaxTotAirVolFlow);
     926              :             }
     927              :         } else {
     928            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     929              :             // Autosized maximum air flow for reporting
     930            7 :             Real64 MaxTotAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     931            7 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     932            7 :             if (MaxTotAirVolFlowDes < SmallAirVolFlow) {
     933            0 :                 MaxTotAirVolFlowDes = 0.0;
     934              :             }
     935            7 :             if (IsAutoSize) {
     936            7 :                 thisPIU.MaxTotAirVolFlow = MaxTotAirVolFlowDes;
     937            7 :                 BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Air Flow Rate [m3/s]", MaxTotAirVolFlowDes);
     938              :             } else {
     939            0 :                 if (thisPIU.MaxTotAirVolFlow > 0.0 && MaxTotAirVolFlowDes > 0.0) {
     940              :                     // Hardsized maximum air flow for reporting
     941            0 :                     Real64 const MaxTotAirVolFlowUser = thisPIU.MaxTotAirVolFlow;
     942            0 :                     BaseSizer::reportSizerOutput(state,
     943              :                                                  thisPIU.UnitType,
     944              :                                                  thisPIU.Name,
     945              :                                                  "Design Size Maximum Air Flow Rate [m3/s]",
     946              :                                                  MaxTotAirVolFlowDes,
     947              :                                                  "User-Specified Maximum Air Flow Rate [m3/s]",
     948              :                                                  MaxTotAirVolFlowUser);
     949            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
     950            0 :                         if ((std::abs(MaxTotAirVolFlowDes - MaxTotAirVolFlowUser) / MaxTotAirVolFlowUser) >
     951            0 :                             state.dataSize->AutoVsHardSizingThreshold) {
     952            0 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
     953            0 :                             ShowContinueError(state, format("User-Specified Maximum Air Flow Rate of {:.5R} [m3/s]", MaxTotAirVolFlowUser));
     954            0 :                             ShowContinueError(state, format("differs from Design Size Maximum Air Flow Rate of {:.5R} [m3/s]", MaxTotAirVolFlowDes));
     955            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     956            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     957              :                         }
     958              :                     }
     959              :                 }
     960              :             }
     961              :         }
     962              :     }
     963              : 
     964              :     // if a sizing run has been done, check if system sizing has been done for this system
     965           15 :     bool SizingDesRunThisAirSys = false;
     966           15 :     if (state.dataSize->SysSizingRunDone) {
     967            6 :         int const AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
     968            6 :         if (AirLoopNum > 0) {
     969            6 :             CheckThisAirSystemForSizing(state, AirLoopNum, SizingDesRunThisAirSys);
     970              :         }
     971              : 
     972              :         // get system sizing id if a sizing run has been done for this system
     973            6 :         if (SizingDesRunThisAirSys) {
     974            6 :             SysSizNum = Util::FindItemInList(
     975            6 :                 state.dataSize->FinalSysSizing(AirLoopNum).AirPriLoopName, state.dataSize->SysSizInput, &SystemSizingInputData::AirPriLoopName);
     976            6 :             if (SysSizNum == 0) {
     977            0 :                 SysSizNum = 1; // use first when none applicable
     978              :             }
     979              :         }
     980              :     }
     981              : 
     982           15 :     IsAutoSize = false;
     983           15 :     if (thisPIU.MaxSecAirVolFlow == AutoSize) {
     984            1 :         IsAutoSize = true;
     985              :     }
     986           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     987            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
     988            0 :             if (thisPIU.MaxSecAirVolFlow > 0.0) {
     989            0 :                 BaseSizer::reportSizerOutput(
     990              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Secondary Air Flow Rate [m3/s]", thisPIU.MaxSecAirVolFlow);
     991              :             }
     992              :         } else {
     993            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     994              :             // Autosized maximum secondary air flow for reporting
     995            7 :             Real64 MaxSecAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     996            7 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     997            7 :             if (MaxSecAirVolFlowDes < SmallAirVolFlow) {
     998            0 :                 MaxSecAirVolFlowDes = 0.0;
     999              :             }
    1000            7 :             if (IsAutoSize) {
    1001            1 :                 thisPIU.MaxSecAirVolFlow = MaxSecAirVolFlowDes;
    1002            1 :                 BaseSizer::reportSizerOutput(
    1003              :                     state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Secondary Air Flow Rate [m3/s]", MaxSecAirVolFlowDes);
    1004              :             } else {
    1005            6 :                 if (thisPIU.MaxSecAirVolFlow > 0.0 && MaxSecAirVolFlowDes > 0.0) {
    1006              :                     // Harsized maximum secondary air flow for reporting
    1007            0 :                     Real64 const MaxSecAirVolFlowUser = thisPIU.MaxSecAirVolFlow;
    1008            0 :                     BaseSizer::reportSizerOutput(state,
    1009              :                                                  thisPIU.UnitType,
    1010              :                                                  thisPIU.Name,
    1011              :                                                  "Design Size Maximum Secondary Air Flow Rate [m3/s]",
    1012              :                                                  MaxSecAirVolFlowDes,
    1013              :                                                  "User-Specified Maximum Secondary Air Flow Rate [m3/s]",
    1014              :                                                  MaxSecAirVolFlowUser);
    1015            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1016            0 :                         if ((std::abs(MaxSecAirVolFlowDes - MaxSecAirVolFlowUser) / MaxSecAirVolFlowUser) >
    1017            0 :                             state.dataSize->AutoVsHardSizingThreshold) {
    1018            0 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
    1019            0 :                             ShowContinueError(state, format("User-Specified Maximum Secondary Air Flow Rate of {:.5R} [m3/s]", MaxSecAirVolFlowUser));
    1020            0 :                             ShowContinueError(
    1021            0 :                                 state, format("differs from Design Size Maximum Secondary Air Flow Rate of {:.5R} [m3/s]", MaxSecAirVolFlowDes));
    1022            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1023            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1024              :                         }
    1025              :                     }
    1026              :                 }
    1027              :             }
    1028              :         }
    1029              :     }
    1030              : 
    1031           15 :     IsAutoSize = false;
    1032           15 :     if (thisPIU.MinPriAirFlowFrac == AutoSize) {
    1033            7 :         IsAutoSize = true;
    1034              :     }
    1035           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1036            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1037            0 :             if (thisPIU.MinPriAirFlowFrac > 0.0) {
    1038            0 :                 BaseSizer::reportSizerOutput(
    1039              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Minimum Primary Air Flow Fraction", thisPIU.MinPriAirFlowFrac);
    1040              :             }
    1041              :         } else {
    1042            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1043              :             // Autosized minimum primary air flow fraction for reporting
    1044            7 :             Real64 MinPriAirFlowFracDes = 0.0;
    1045           14 :             if (thisPIU.MaxPriAirVolFlow >= SmallAirVolFlow &&
    1046            7 :                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA >= SmallAirVolFlow) {
    1047            6 :                 MinPriAirFlowFracDes = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA / thisPIU.MaxPriAirVolFlow;
    1048              :             }
    1049            7 :             if (SizingDesRunThisAirSys) {
    1050            6 :                 if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) { // 62.1 simplified procedure
    1051            5 :                     if (thisPIU.MaxPriAirVolFlow > 0.0) {
    1052            5 :                         MinPriAirFlowFracDes = 1.5 *
    1053            5 :                                                max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozClgByZone,
    1054            5 :                                                    state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozHtgByZone) /
    1055            5 :                                                thisPIU.MaxPriAirVolFlow;
    1056              : 
    1057              :                         // adjust maximum flow rate
    1058            5 :                         if (MinPriAirFlowFracDes > 1.0 && IsMaxPriFlowAutoSize) {
    1059            4 :                             thisPIU.MaxPriAirVolFlow *= MinPriAirFlowFracDes;
    1060            4 :                             MinPriAirFlowFracDes = 1.0;
    1061            8 :                             ShowWarningError(state,
    1062            8 :                                              format("SingleDuctSystem:SizeSys: Autosized maximum air flow rate for {} was increased to meet the zone "
    1063              :                                                     "primary air flow determined according to the ASHRAE Standard 62.1 Simplified Procedure.",
    1064            4 :                                                     thisPIU.Name));
    1065            1 :                         } else if (MinPriAirFlowFracDes > 1.0) {
    1066            2 :                             ShowWarningError(
    1067              :                                 state,
    1068            2 :                                 format("SingleDuctSystem:SizeSys: Maximum primary air flow rate for {} is potentially too low.", thisPIU.Name));
    1069            2 :                             ShowContinueError(state,
    1070              :                                               "The flow is lower than the minimum primary air flow rate calculated following the ASHRAE Standard "
    1071              :                                               "62.1 Simplified Procedure:");
    1072            1 :                             ShowContinueError(state, format(" User-specified maximum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow));
    1073            2 :                             ShowContinueError(
    1074              :                                 state,
    1075            2 :                                 format(" Calculated minimum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow * MinPriAirFlowFracDes));
    1076            1 :                             MinPriAirFlowFracDes = 1.0;
    1077              :                         }
    1078              :                     }
    1079              :                 }
    1080              :             }
    1081            7 :             if (IsAutoSize) {
    1082            7 :                 if (SizingDesRunThisAirSys) {
    1083            6 :                     if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) {
    1084            5 :                         state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VpzMinByZoneSPSized = true;
    1085              :                     }
    1086              :                 }
    1087            7 :                 thisPIU.MinPriAirFlowFrac = MinPriAirFlowFracDes;
    1088            7 :                 BaseSizer::reportSizerOutput(
    1089              :                     state, thisPIU.UnitType, thisPIU.Name, "Design Size Minimum Primary Air Flow Fraction", MinPriAirFlowFracDes);
    1090              :             } else {
    1091            0 :                 if (thisPIU.MinPriAirFlowFrac > 0.0 && MinPriAirFlowFracDes > 0.0) {
    1092              :                     // Hardsized minimum primary air flow fraction for reporting
    1093            0 :                     Real64 const MinPriAirFlowFracUser = thisPIU.MinPriAirFlowFrac;
    1094            0 :                     BaseSizer::reportSizerOutput(state,
    1095              :                                                  thisPIU.UnitType,
    1096              :                                                  thisPIU.Name,
    1097              :                                                  "Design Size Minimum Primary Air Flow Fraction",
    1098              :                                                  MinPriAirFlowFracDes,
    1099              :                                                  "User-Specified Minimum Primary Air Flow Fraction",
    1100              :                                                  MinPriAirFlowFracUser);
    1101            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1102            0 :                         if ((std::abs(MinPriAirFlowFracDes - MinPriAirFlowFracUser) / MinPriAirFlowFracUser) >
    1103            0 :                             state.dataSize->AutoVsHardSizingThreshold) {
    1104            0 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
    1105            0 :                             ShowContinueError(state, format("User-Specified Minimum Primary Air Flow Fraction of {:.1R}", MinPriAirFlowFracUser));
    1106            0 :                             ShowContinueError(state,
    1107            0 :                                               format("differs from Design Size Minimum Primary Air Flow Fraction of {:.1R}", MinPriAirFlowFracDes));
    1108            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1109            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1110              :                         }
    1111              :                     }
    1112              :                 }
    1113              :             }
    1114              :         }
    1115              :     }
    1116              : 
    1117           15 :     if (CurTermUnitSizingNum > 0) {
    1118              : 
    1119            7 :         if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat) {
    1120            7 :             TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxTotAirVolFlow;
    1121            0 :         } else if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat) {
    1122            0 :             TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxSecAirVolFlow + thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
    1123              :         }
    1124              :     }
    1125              : 
    1126           15 :     IsAutoSize = false;
    1127           15 :     if (thisPIU.FanOnFlowFrac == AutoSize) {
    1128            1 :         IsAutoSize = true;
    1129              :     }
    1130           15 :     if (state.dataSize->CurZoneEqNum > 0) {
    1131            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1132            0 :             if (thisPIU.FanOnFlowFrac > 0.0) {
    1133            0 :                 BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "User-Specified Fan On Flow Fraction", thisPIU.FanOnFlowFrac);
    1134              :             }
    1135              :         } else {
    1136            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1137              :             // Autosized fan on flow fraction for reporting
    1138            7 :             Real64 FanOnFlowFracDes = thisPIU.MinPriAirFlowFrac;
    1139            7 :             if (IsAutoSize) {
    1140            1 :                 thisPIU.FanOnFlowFrac = FanOnFlowFracDes;
    1141            1 :                 BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Fan On Flow Fraction", FanOnFlowFracDes);
    1142              :             } else {
    1143            6 :                 if (thisPIU.FanOnFlowFrac > 0.0 && FanOnFlowFracDes > 0.0) {
    1144              :                     // Hardsized fan on flow fraction for reporting
    1145            0 :                     Real64 const FanOnFlowFracUser = thisPIU.FanOnFlowFrac;
    1146            0 :                     BaseSizer::reportSizerOutput(state,
    1147              :                                                  thisPIU.UnitType,
    1148              :                                                  thisPIU.Name,
    1149              :                                                  "Design Size Fan On Flow Fraction",
    1150              :                                                  FanOnFlowFracDes,
    1151              :                                                  "User-Specified Fan On Flow Fraction",
    1152              :                                                  FanOnFlowFracUser);
    1153            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
    1154            0 :                         if ((std::abs(FanOnFlowFracDes - FanOnFlowFracUser) / FanOnFlowFracUser) > state.dataSize->AutoVsHardSizingThreshold) {
    1155            0 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
    1156            0 :                             ShowContinueError(state, format("User-Specified Fan On Flow Fraction of {:.1R}", FanOnFlowFracUser));
    1157            0 :                             ShowContinueError(state, format("differs from Design Size Fan On Flow Fraction of {:.1R}", FanOnFlowFracDes));
    1158            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1159            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1160              :                         }
    1161              :                     }
    1162              :                 }
    1163              :             }
    1164              :         }
    1165              :     }
    1166              : 
    1167           15 :     IsAutoSize = false;
    1168           15 :     if (thisPIU.MaxVolHotWaterFlow == AutoSize) { //.or.()) THEN
    1169            6 :         IsAutoSize = true;
    1170              :     }
    1171           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1172            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1173            0 :             if (thisPIU.MaxVolHotWaterFlow > 0.0) {
    1174            0 :                 BaseSizer::reportSizerOutput(
    1175              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Reheat Water Flow Rate [m3/s]", thisPIU.MaxVolHotWaterFlow);
    1176              :             }
    1177              :         } else {
    1178            7 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1179            7 :             if (Util::SameString(HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], "Coil:Heating:Water")) {
    1180              : 
    1181            0 :                 int const CoilWaterInletNode = GetCoilWaterInletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
    1182            0 :                 int const CoilWaterOutletNode = GetCoilWaterOutletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
    1183              : 
    1184              :                 // Autosized maximum hot water flow for reporting
    1185            0 :                 Real64 MaxVolHotWaterFlowDes = 0.0;
    1186              : 
    1187            0 :                 if (IsAutoSize) {
    1188              :                     int const PltSizHeatNum =
    1189            0 :                         MyPlantSizingIndex(state, "Coil:Heating:Water", thisPIU.HCoil, CoilWaterInletNode, CoilWaterOutletNode, ErrorsFound);
    1190            0 :                     if (PltSizHeatNum > 0) {
    1191              : 
    1192            0 :                         if (state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatMassFlow >= SmallAirVolFlow) {
    1193              :                             Real64 const CoilInTemp =
    1194            0 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU * thisPIU.MinPriAirFlowFrac +
    1195            0 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).ZoneTempAtHeatPeak * (1.0 - thisPIU.MinPriAirFlowFrac);
    1196            0 :                             Real64 const CoilOutTemp = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesTemp;
    1197            0 :                             Real64 const CoilOutHumRat = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesHumRat;
    1198            0 :                             Real64 const DesMassFlow = state.dataEnvrn->StdRhoAir * TermUnitSizing(CurTermUnitSizingNum).AirVolFlow;
    1199            0 :                             DesCoilLoad = PsyCpAirFnW(CoilOutHumRat) * DesMassFlow * (CoilOutTemp - CoilInTemp);
    1200              : 
    1201            0 :                             Real64 const rho = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum)
    1202            0 :                                                    .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
    1203            0 :                             Real64 const Cp = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum)
    1204            0 :                                                   .glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineName);
    1205              : 
    1206            0 :                             MaxVolHotWaterFlowDes = DesCoilLoad / (state.dataSize->PlantSizData(PltSizHeatNum).DeltaT * Cp * rho);
    1207              :                         } else {
    1208            0 :                             MaxVolHotWaterFlowDes = 0.0;
    1209              :                         }
    1210              :                     } else {
    1211            0 :                         ShowSevereError(state, "Autosizing of water flow requires a heating loop Sizing:Plant object");
    1212            0 :                         ShowContinueError(state, format("Occurs in{} Object={}", thisPIU.UnitType, thisPIU.Name));
    1213            0 :                         ErrorsFound = true;
    1214              :                     }
    1215            0 :                     thisPIU.MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
    1216            0 :                     BaseSizer::reportSizerOutput(
    1217              :                         state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Reheat Water Flow Rate [m3/s]", MaxVolHotWaterFlowDes);
    1218            0 :                     BaseSizer::reportSizerOutput(state,
    1219              :                                                  thisPIU.UnitType,
    1220              :                                                  thisPIU.Name,
    1221              :                                                  "Design Size Reheat Coil Inlet Air Temperature [C]",
    1222            0 :                                                  state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU);
    1223            0 :                     BaseSizer::reportSizerOutput(state,
    1224              :                                                  thisPIU.UnitType,
    1225              :                                                  thisPIU.Name,
    1226              :                                                  "Design Size Reheat Coil Inlet Air Humidity Ratio [kgWater/kgDryAir]",
    1227            0 :                                                  state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
    1228              :                 } else { // Hardsize with sizing data
    1229            0 :                     if (thisPIU.MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
    1230              :                         // Hardsized maximum hot water flow for reporting
    1231            0 :                         Real64 const MaxVolHotWaterFlowUser = thisPIU.MaxVolHotWaterFlow;
    1232            0 :                         BaseSizer::reportSizerOutput(state,
    1233              :                                                      thisPIU.UnitType,
    1234              :                                                      thisPIU.Name,
    1235              :                                                      "Design Size Maximum Reheat Water Flow Rate [m3/s]",
    1236              :                                                      MaxVolHotWaterFlowDes,
    1237              :                                                      "User-Specified Maximum Reheat Water Flow Rate [m3/s]",
    1238              :                                                      MaxVolHotWaterFlowUser);
    1239            0 :                         if (state.dataGlobal->DisplayExtraWarnings) {
    1240            0 :                             if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
    1241            0 :                                 state.dataSize->AutoVsHardSizingThreshold) {
    1242            0 :                                 ShowMessage(state,
    1243            0 :                                             format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
    1244            0 :                                 ShowContinueError(state,
    1245            0 :                                                   format("User-Specified Maximum Reheat Water Flow Rate of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
    1246            0 :                                 ShowContinueError(
    1247            0 :                                     state, format("differs from Design Size Maximum Reheat Water Flow Rate of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
    1248            0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1249            0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1250              :                             }
    1251              :                         }
    1252              :                     }
    1253              :                 }
    1254              :             } else {
    1255            7 :                 thisPIU.MaxVolHotWaterFlow = 0.0;
    1256              :             }
    1257              :         }
    1258              :     }
    1259              : 
    1260           15 :     IsAutoSize = false;
    1261           15 :     if (thisPIU.MaxVolHotSteamFlow == AutoSize) {
    1262            1 :         IsAutoSize = true;
    1263              :     }
    1264           15 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1265            7 :         if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
    1266            0 :             if (thisPIU.MaxVolHotWaterFlow > 0.0) {
    1267            0 :                 BaseSizer::reportSizerOutput(
    1268              :                     state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Reheat Steam Flow Rate [m3/s]", thisPIU.MaxVolHotWaterFlow);
    1269              :             }
    1270              :         } else {
    1271            7 :             if (Util::SameString(HCoilNames[static_cast<int>(thisPIU.HCoilType)], "Coil:Heating:Steam")) {
    1272              : 
    1273            0 :                 int const CoilSteamInletNode = GetCoilSteamInletNode(state, "Coil:Heating:Steam", thisPIU.HCoil, ErrorsFound);
    1274            0 :                 int const CoilSteamOutletNode = GetCoilSteamOutletNode(state, "Coil:Heating:Steam", thisPIU.HCoil, ErrorsFound);
    1275            0 :                 Real64 MaxVolHotSteamFlowDes = 0.0; // Autosized maximum hot steam flow for reporting
    1276              : 
    1277            0 :                 if (IsAutoSize) {
    1278              :                     int const PltSizHeatNum =
    1279            0 :                         MyPlantSizingIndex(state, "Coil:Heating:Steam", thisPIU.HCoil, CoilSteamInletNode, CoilSteamOutletNode, ErrorsFound);
    1280            0 :                     if (PltSizHeatNum > 0) {
    1281            0 :                         if (state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatMassFlow >= SmallAirVolFlow) {
    1282              :                             Real64 const CoilInTemp =
    1283            0 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU * thisPIU.MinPriAirFlowFrac +
    1284            0 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).ZoneTempAtHeatPeak * (1.0 - thisPIU.MinPriAirFlowFrac);
    1285            0 :                             Real64 const CoilOutTemp = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesTemp;
    1286            0 :                             Real64 const CoilOutHumRat = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesHumRat;
    1287            0 :                             Real64 const DesMassFlow = state.dataEnvrn->StdRhoAir * TermUnitSizing(CurTermUnitSizingNum).AirVolFlow;
    1288            0 :                             DesCoilLoad = PsyCpAirFnW(CoilOutHumRat) * DesMassFlow * (CoilOutTemp - CoilInTemp);
    1289            0 :                             Real64 constexpr TempSteamIn = 100.00;
    1290            0 :                             Real64 const EnthSteamInDry = thisPIU.HCoil_fluid->getSatEnthalpy(state, TempSteamIn, 1.0, RoutineName);
    1291            0 :                             Real64 const EnthSteamOutWet = thisPIU.HCoil_fluid->getSatEnthalpy(state, TempSteamIn, 0.0, RoutineName);
    1292            0 :                             Real64 const LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
    1293            0 :                             Real64 const SteamDensity = thisPIU.HCoil_fluid->getSatDensity(state, TempSteamIn, 1.0, RoutineName);
    1294              :                             Real64 const Cp =
    1295            0 :                                 Fluid::GetWater(state)->getSpecificHeat(state, state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp, RoutineName);
    1296            0 :                             MaxVolHotSteamFlowDes =
    1297            0 :                                 DesCoilLoad / (SteamDensity * (LatentHeatSteam + state.dataSize->PlantSizData(PltSizHeatNum).DeltaT * Cp));
    1298              :                         } else {
    1299            0 :                             MaxVolHotSteamFlowDes = 0.0;
    1300              :                         }
    1301              :                     } else {
    1302            0 :                         ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
    1303            0 :                         ShowContinueError(state, format("Occurs in{} Object={}", thisPIU.UnitType, thisPIU.Name));
    1304            0 :                         ErrorsFound = true;
    1305              :                     }
    1306            0 :                     thisPIU.MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
    1307            0 :                     BaseSizer::reportSizerOutput(
    1308              :                         state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Reheat Steam Flow [m3/s]", MaxVolHotSteamFlowDes);
    1309              :                 } else {
    1310            0 :                     if (thisPIU.MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
    1311            0 :                         Real64 const MaxVolHotSteamFlowUser = thisPIU.MaxVolHotSteamFlow;
    1312            0 :                         BaseSizer::reportSizerOutput(state,
    1313              :                                                      thisPIU.UnitType,
    1314              :                                                      thisPIU.Name,
    1315              :                                                      "Design Size Maximum Reheat Steam Flow [m3/s]",
    1316              :                                                      MaxVolHotSteamFlowDes,
    1317              :                                                      "User-Specified Maximum Reheat Steam Flow [m3/s]",
    1318              :                                                      MaxVolHotSteamFlowUser);
    1319            0 :                         if (state.dataGlobal->DisplayExtraWarnings) {
    1320            0 :                             if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
    1321            0 :                                 state.dataSize->AutoVsHardSizingThreshold) {
    1322            0 :                                 ShowMessage(state,
    1323            0 :                                             format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
    1324            0 :                                 ShowContinueError(state, format("User-Specified Maximum Reheat Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
    1325            0 :                                 ShowContinueError(
    1326            0 :                                     state, format("differs from Design Size Maximum Reheat Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
    1327            0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
    1328            0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
    1329              :                             }
    1330              :                         }
    1331              :                     }
    1332              :                 }
    1333              :             } else {
    1334            7 :                 thisPIU.MaxVolHotSteamFlow = 0.0;
    1335              :             }
    1336              :         }
    1337              :     }
    1338              : 
    1339           15 :     if (CurTermUnitSizingNum > 0) {
    1340            7 :         TermUnitSizing(CurTermUnitSizingNum).MinPriFlowFrac = thisPIU.MinPriAirFlowFrac;
    1341            7 :         TermUnitSizing(CurTermUnitSizingNum).plenumIndex = thisPIU.plenumIndex;
    1342            7 :         TermUnitSizing(CurTermUnitSizingNum).MaxHWVolFlow = thisPIU.MaxVolHotWaterFlow;
    1343            7 :         TermUnitSizing(CurTermUnitSizingNum).MaxSTVolFlow = thisPIU.MaxVolHotSteamFlow;
    1344            7 :         TermUnitSizing(CurTermUnitSizingNum).DesHeatingLoad = DesCoilLoad; // coil report
    1345            7 :         TermUnitSizing(CurTermUnitSizingNum).InducesPlenumAir = thisPIU.InducesPlenumAir;
    1346            7 :         if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
    1347            0 :             SetCoilDesFlow(state,
    1348            0 :                            HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)],
    1349            0 :                            thisPIU.HCoil,
    1350            0 :                            TermUnitSizing(CurTermUnitSizingNum).AirVolFlow,
    1351              :                            ErrorsFound);
    1352              :         }
    1353              :     }
    1354              : 
    1355           15 :     if (ErrorsFound) {
    1356            0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
    1357              :     }
    1358           15 : }
    1359              : 
    1360        15932 : void CalcSeriesPIU(EnergyPlusData &state,
    1361              :                    int const PIUNum,             // number of the current PIU being simulated
    1362              :                    int const ZoneNum,            // number of zone being served
    1363              :                    int const ZoneNode,           // zone node number
    1364              :                    bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
    1365              : )
    1366              : {
    1367              : 
    1368              :     // SUBROUTINE INFORMATION:
    1369              :     //       AUTHOR         Fred Buhl
    1370              :     //       DATE WRITTEN   August 2000
    1371              :     //       MODIFIED       na
    1372              :     //       RE-ENGINEERED  na
    1373              : 
    1374              :     // PURPOSE OF THIS SUBROUTINE:
    1375              :     // Simulate a series powered induction unit; adjust its primary air flow
    1376              :     // and reheat coil output to match the zone load.
    1377              : 
    1378              :     // METHODOLOGY EMPLOYED:
    1379              :     // If unit is on and there is a cooling load:
    1380              :     // (1) simulates mixer and fan at max secondary air flow and heating coil
    1381              :     //     off. Obtains fan temperature increase.
    1382              :     // (2) Calculates primary and secomdary air flow to meet zone load and
    1383              :     //     resimulates mixer, fan, and (off) coil.
    1384              :     // If unit is on and there is a heating load
    1385              :     // (1) sets primary air flow to a minimum.
    1386              :     // (2) simulates mixer and fan
    1387              :     // (3) if reheat is hot water, calls ControlCompOutput to simulate hot
    1388              :     //     water coil and adjust water flow to match coil output to the zone load.
    1389              :     // (4) if reheat is electric or gas calls SimulateHeatingCoilComponents to
    1390              :     //     simulate coil at coil output that matches the zone load
    1391              : 
    1392              :     // REFERENCES:
    1393              :     // na
    1394              : 
    1395              :     // Using/Aliasing
    1396              :     using namespace DataZoneEnergyDemands;
    1397              :     using HeatingCoils::SimulateHeatingCoilComponents;
    1398              :     using MixerComponent::SimAirMixer;
    1399              :     using PlantUtilities::SetComponentFlowRate;
    1400              :     using SteamCoils::SimulateSteamCoilComponents;
    1401              :     using WaterCoils::SimulateWaterCoilComponents;
    1402              : 
    1403              :     // Locals
    1404              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    1405              : 
    1406              :     // SUBROUTINE PARAMETER DEFINITIONS:
    1407              : 
    1408              :     // INTERFACE BLOCK SPECIFICATIONS
    1409              : 
    1410              :     // DERIVED TYPE DEFINITIONS
    1411              :     // na
    1412              : 
    1413              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1414        15932 :     bool UnitOn(true); // TRUE if unit is on
    1415        15932 :     bool PriOn(true);  // TRUE if primary air available
    1416              : 
    1417        15932 :     Real64 QCoilReq = 0.0;     // required heating coil outlet to meet zone load
    1418        15932 :     Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
    1419        15932 :     Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
    1420              : 
    1421              :     // initialize local variables
    1422        15932 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    1423              : 
    1424        15932 :     Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
    1425        15932 :     Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
    1426              :     Real64 const QZnReq =
    1427        15932 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
    1428              :     Real64 const QToHeatSetPt =
    1429        15932 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W]  remaining load to heating setpoint
    1430        15932 :     Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);          // zone air specific heat [J/kg-C]
    1431        15932 :     thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;   // primary air mass flow rate [kg/s]
    1432        15932 :     thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;   // secondary air mass flow rate [kg/s]
    1433        15932 :     if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1434            8 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1435              :     } else {
    1436        15924 :         thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1437              :     }
    1438        15932 :     thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
    1439              : 
    1440              :     // On the first HVAC iteration the system values are given to the controller, but after that
    1441              :     // the demand limits are in place and there needs to be feedback to the Zone Equipment
    1442        15932 :     if (thisPIU.HotControlNode > 0) {
    1443            0 :         if (FirstHVACIteration) {
    1444            0 :             MaxWaterFlow = thisPIU.MaxHotWaterFlow;
    1445            0 :             MinWaterFlow = thisPIU.MinHotWaterFlow;
    1446              :         } else {
    1447            0 :             MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
    1448            0 :             MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
    1449              :         }
    1450              :     }
    1451        15932 :     if (thisPIU.availSched->getCurrentVal() <= 0.0) {
    1452            0 :         UnitOn = false;
    1453              :     }
    1454        15932 :     if ((thisPIU.fanAvailSched->getCurrentVal() <= 0.0 || state.dataHVACGlobal->TurnFansOff) && !state.dataHVACGlobal->TurnFansOn) {
    1455            1 :         UnitOn = false;
    1456              :     }
    1457        15932 :     if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
    1458           37 :         PriOn = false;
    1459              :     }
    1460              :     // Set the mass flow rates
    1461        15932 :     if (UnitOn) {
    1462              :         // unit is on
    1463        15931 :         if (!PriOn) {
    1464              :             // no primary air flow
    1465           36 :             thisPIU.PriAirMassFlow = 0.0;
    1466              :             // PIU fan off if there is no heating load, also reset fan flag if fan should be off
    1467           36 :             if (QZnReq <= SmallLoad) {
    1468           22 :                 thisPIU.SecAirMassFlow = 0.0;
    1469           22 :                 state.dataHVACGlobal->TurnFansOn = false;
    1470              :             } else {
    1471           14 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1472            0 :                     thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1473            0 :                     CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1474           14 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1475            0 :                            thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1476            0 :                     CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1477           14 :                 } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1478           14 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1479           14 :                     thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow;
    1480              :                 }
    1481              :             }
    1482        15895 :         } else if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) || std::abs(QZnReq) < SmallLoad) {
    1483              :             // in deadband or very small load: set primary air flow to the minimum
    1484         1470 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1485         1470 :             if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1486         1470 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1487         1470 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1488            0 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1489            0 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.PriAirMassFlow);
    1490              :             }
    1491        14425 :         } else if (QZnReq > SmallLoad) {
    1492              :             // heating: set primary air flow to the minimum
    1493         7026 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1494              :             // determine secondary flow rate
    1495         7026 :             if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1496            6 :                 thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1497            2 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1498         7024 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1499            4 :                        thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1500            4 :                 CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1501         7020 :             } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1502         7020 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1503         7020 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1504              :             }
    1505              :         } else {
    1506         7399 :             if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1507              :                 // cooling: set the primary air flow rate to meet the load.
    1508              :                 // First calculate the fan temperature rise
    1509              :                 // use only secondary air for this calculation
    1510         7397 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1511         7397 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    1512         7397 :                 SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
    1513         7397 :                 state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1514              : 
    1515              :                 // fan temperature rise [C]
    1516              :                 Real64 const FanDeltaTemp =
    1517         7397 :                     state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp;
    1518              : 
    1519              :                 // using the required zone load, calculate the air temperature needed to meet the load
    1520         7397 :                 Real64 const OutletTempNeeded = state.dataLoopNodes->Node(ZoneNode).Temp + QZnReq / (thisPIU.MaxTotAirMassFlow * CpAirZn);
    1521              : 
    1522              :                 // mixer outlet temperature needed to meet cooling load
    1523         7397 :                 Real64 const MixTempNeeded = OutletTempNeeded - FanDeltaTemp;
    1524              : 
    1525         7397 :                 if (MixTempNeeded <= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp) { //
    1526         7381 :                     thisPIU.PriAirMassFlow = PriAirMassFlowMax;
    1527           32 :                 } else if (MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp &&
    1528           16 :                            MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp) {
    1529            0 :                     thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1530              :                 } else {
    1531           16 :                     thisPIU.PriAirMassFlow =
    1532           16 :                         thisPIU.MaxTotAirMassFlow * (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - MixTempNeeded) /
    1533           16 :                         max(SmallTempDiff,
    1534           16 :                             state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp);
    1535           16 :                     thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1536              :                 }
    1537         7397 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1538         7397 :                 if (QZnReq < 0) {
    1539         7397 :                     thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
    1540              :                 }
    1541            2 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1542            2 :                 CalcVariableSpeedPIUCoolingBehavior(state, PIUNum, ZoneNode, QZnReq, QToHeatSetPt, PriAirMassFlowMin, PriAirMassFlowMax);
    1543              :             }
    1544              :         }
    1545              :     } else {
    1546              :         // unit is off ; no flow
    1547            1 :         thisPIU.PriAirMassFlow = 0.0;
    1548            1 :         thisPIU.SecAirMassFlow = 0.0;
    1549              :     }
    1550              :     // set inlet node flowrates
    1551        15932 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
    1552        15932 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
    1553        15932 :     if (PriAirMassFlowMax == 0) {
    1554           37 :         thisPIU.PriDamperPosition = 0;
    1555              :     } else {
    1556        15895 :         thisPIU.PriDamperPosition = thisPIU.PriAirMassFlow / PriAirMassFlowMax;
    1557              :     }
    1558              : 
    1559              :     // now that inlet airflows have been set, the terminal components can be simulated.
    1560              :     // fire the mixer
    1561        15932 :     SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    1562              : 
    1563              :     // fire the fan
    1564        15932 :     if (thisPIU.fanType == HVAC::FanType::SystemModel) {
    1565        13817 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1566              :             // calculate fan speed ratio
    1567            8 :             Real64 fanFlowRatio(1.0);
    1568            8 :             if (thisPIU.MaxTotAirMassFlow > 0.0) {
    1569            8 :                 fanFlowRatio = (thisPIU.PriAirMassFlow + thisPIU.SecAirMassFlow) / thisPIU.MaxTotAirMassFlow;
    1570              :             }
    1571            8 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
    1572              :         } else {
    1573        13809 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1574              :         }
    1575         2115 :     } else if (thisPIU.fanType == HVAC::FanType::Constant) {
    1576         2115 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1577              :     }
    1578              : 
    1579              :     // the heating load seen by the reheat coil [W]
    1580        15932 :     Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
    1581        15932 :                                                (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
    1582              : 
    1583              :     // check if heating coil is off
    1584        15931 :     if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleCool) ||
    1585        31867 :          (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
    1586         9119 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1587         9118 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1588              :     }
    1589              : 
    1590              :     // determine what is required of heater for current operating stage
    1591        15932 :     if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
    1592         9118 :         QCoilReq = 0.0;
    1593         6814 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
    1594            1 :         QCoilReq = 0.0;
    1595         6813 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
    1596         6808 :         QCoilReq = QActualHeating;
    1597            5 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
    1598            1 :         QCoilReq = QActualHeating;
    1599            4 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
    1600            1 :         QCoilReq = QActualHeating;
    1601            3 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
    1602              :         // find heater power to deliver design discharge air temperature
    1603            1 :         Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1604              :         Real64 mixAirEnthalpy =
    1605            1 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1606            1 :         QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
    1607            2 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
    1608              :         // find heater power to deliver maximum discharge air temperature
    1609            2 :         Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1610              :         Real64 mixAirEnthalpy =
    1611            2 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1612            2 :         Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
    1613            2 :         if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
    1614            1 :             QCoilReq = QcoilLimit;
    1615              :         } else {
    1616            1 :             QCoilReq = QActualHeating;
    1617              :         }
    1618              :     } else {
    1619            0 :         ShowSevereError(state, "Incorrect series PIU heating operation.");
    1620            0 :         ShowFatalError(state, format("Series PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    1621              :     }
    1622        15932 :     if ((QCoilReq < SmallLoad) &&
    1623         9119 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1624         9118 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1625         9118 :         QCoilReq = 0.0;
    1626              :     }
    1627              : 
    1628              :     // fire the heating coil
    1629        15932 :     switch (thisPIU.HCoilType) {
    1630            0 :     case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
    1631            0 :         if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
    1632              :             // call the reheat coil with the NO FLOW condition
    1633            0 :             Real64 mdot = 0.0;
    1634            0 :             SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
    1635              : 
    1636            0 :             SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
    1637            0 :         } else {
    1638              :             // control water flow to obtain output matching QZnReq
    1639            0 :             ControlCompOutput(state,
    1640            0 :                               thisPIU.HCoil,
    1641            0 :                               thisPIU.UnitType,
    1642            0 :                               thisPIU.HCoil_Index,
    1643              :                               FirstHVACIteration,
    1644              :                               QCoilReq,
    1645              :                               thisPIU.HotControlNode,
    1646              :                               MaxWaterFlow,
    1647              :                               MinWaterFlow,
    1648              :                               thisPIU.HotControlOffset,
    1649            0 :                               thisPIU.ControlCompTypeNum,
    1650            0 :                               thisPIU.CompErrIndex,
    1651            0 :                               thisPIU.HCoilInAirNode,
    1652            0 :                               thisPIU.OutAirNode,
    1653              :                               _,
    1654              :                               _,
    1655              :                               _,
    1656            0 :                               thisPIU.HWplantLoc);
    1657              :         }
    1658            0 :         break;
    1659              :     }
    1660            0 :     case HtgCoilType::SteamAirHeating: { // COIL:STEAM:AIRHEATING
    1661            0 :         SimulateSteamCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index, QCoilReq);
    1662            0 :         break;
    1663              :     }
    1664         2124 :     case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
    1665         2124 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    1666         2124 :         break;
    1667              :     }
    1668        13808 :     case HtgCoilType::Gas: { // COIL:GAS:HEATING
    1669        13808 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    1670        13808 :         break;
    1671              :     }
    1672            0 :     default:
    1673            0 :         break;
    1674              :     }
    1675              : 
    1676              :     // Power supplied
    1677        15932 :     Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
    1678        15932 :                       (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
    1679        15932 :                        PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
    1680        15932 :     thisPIU.HeatingRate = max(0.0, PowerMet);
    1681        15932 :     thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
    1682        15932 :     thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
    1683        15932 :     thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    1684        15932 :     thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
    1685        15932 :     thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
    1686        15932 :     if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
    1687           29 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1688           29 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
    1689              :     }
    1690        15932 :     if (thisPIU.InducesPlenumAir) {
    1691            1 :         state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    1692              :     } else {
    1693        15931 :         state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
    1694              :     }
    1695        15932 :     state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
    1696        15932 :     state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
    1697              : 
    1698        15932 :     ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
    1699        15932 : }
    1700              : 
    1701           14 : void CalcParallelPIU(EnergyPlusData &state,
    1702              :                      int const PIUNum,             // number of the current PIU being simulated
    1703              :                      int const ZoneNum,            // number of zone being served
    1704              :                      int const ZoneNode,           // zone node number
    1705              :                      bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
    1706              : )
    1707              : {
    1708              : 
    1709              :     // SUBROUTINE INFORMATION:
    1710              :     //       AUTHOR         Fred Buhl
    1711              :     //       DATE WRITTEN   August 2000
    1712              :     //       MODIFIED       September 2016, March 2017
    1713              : 
    1714              :     // PURPOSE OF THIS SUBROUTINE:
    1715              :     // Simulate a parallel powered induction unit; adjust its primary air flow
    1716              :     // and reheat coil output to match the zone load.
    1717              : 
    1718              :     // METHODOLOGY EMPLOYED:
    1719              :     // If unit is on and there is a cooling load:
    1720              :     // (1) simulate fan at max secondary air flow and heating coil
    1721              :     //     off. Obtains fan temperature increase.
    1722              :     // (2) Calculates primary and secomdary air flow to meet zone load.
    1723              :     //     (a) Assume fan is off and calculate primary air flow to meet cooling load.
    1724              :     //     (b1) If calculated primary air flow is above the fan turn on ratio, fan is off.
    1725              :     //         Otherwise fan is on; calculate mixed secondary and primary air flow that
    1726              :     //         will meet the zone load
    1727              :     //     (b2) If the fan turn on ratio is zero, then the fan is on only if reheat is needed.
    1728              :     //  (3) Simulate fan, mixer, and (off) heating coil to obtain zone inlet conditions.
    1729              :     // If unit is on and there is a heating load
    1730              :     // (1) sets primary air flow to a minimum.
    1731              :     // (2) simulates fan and mixer
    1732              :     // (3) if reheat is hot water, calls ControlCompOutput to simulate hot
    1733              :     //     water coil and adjust water flow to match coil output to the zone load.
    1734              :     // (4) if reheat is electric or gas calls SimulateHeatingCoilComponents to
    1735              :     //     simulate coil at coil output that matches the zone load
    1736              : 
    1737              :     using namespace DataZoneEnergyDemands;
    1738              :     using HeatingCoils::SimulateHeatingCoilComponents;
    1739              :     using MixerComponent::SimAirMixer;
    1740              :     using PlantUtilities::SetComponentFlowRate;
    1741              :     using SteamCoils::SimulateSteamCoilComponents;
    1742              :     using WaterCoils::SimulateWaterCoilComponents;
    1743              : 
    1744           14 :     bool UnitOn(true); // TRUE if unit is on
    1745           14 :     bool PriOn(true);  // TRUE if primary air available
    1746              : 
    1747           14 :     Real64 QCoilReq = 0.0;     // required heating coil outlet to meet zone load
    1748           14 :     Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
    1749           14 :     Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
    1750              : 
    1751              :     // initialize local variables
    1752           14 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    1753              : 
    1754           14 :     Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
    1755           14 :     Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
    1756           14 :     thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;                 // primary air mass flow rate [kg/s]
    1757           14 :     thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;                 // secondary air mass flow rate [kg/s]
    1758              :     Real64 const QZnReq =
    1759           14 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
    1760              :     Real64 const QToHeatSetPt =
    1761           14 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W]  remaining load to heating setpoint
    1762           14 :     Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);          // zone air specific heat [J/kg-C]
    1763           14 :     thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1764           14 :     thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
    1765              : 
    1766              :     // On the first HVAC iteration the system values are given to the controller, but after that
    1767              :     // the demand limits are in place and there needs to be feedback to the Zone Equipment
    1768           14 :     if (thisPIU.HotControlNode > 0) {
    1769            0 :         if (FirstHVACIteration) {
    1770            0 :             MaxWaterFlow = thisPIU.MaxHotWaterFlow;
    1771            0 :             MinWaterFlow = thisPIU.MinHotWaterFlow;
    1772              :         } else {
    1773            0 :             MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
    1774            0 :             MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
    1775              :         }
    1776              :     }
    1777           14 :     if (thisPIU.availSched->getCurrentVal() <= 0.0) {
    1778            0 :         UnitOn = false;
    1779              :     }
    1780           14 :     if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
    1781            5 :         PriOn = false;
    1782              :     }
    1783              :     // Set the mass flow rates
    1784           14 :     if (UnitOn) {
    1785              :         // unit is on
    1786              :         // Calculate if reheat is needed
    1787           14 :         bool ReheatRequired = false;
    1788              :         Real64 const qMinPrimary =
    1789              :             PriAirMassFlowMin *
    1790           14 :             (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1791           14 :         if (qMinPrimary < QToHeatSetPt) {
    1792            9 :             ReheatRequired = true;
    1793              :         }
    1794              : 
    1795           14 :         if (!PriOn) {
    1796              :             // no primary air flow
    1797            5 :             thisPIU.PriAirMassFlow = 0.0;
    1798              :             // PIU fan off if there is no heating load, also reset fan flag if fan should be off
    1799            5 :             if (QZnReq <= SmallLoad) {
    1800            2 :                 thisPIU.SecAirMassFlow = 0.0;
    1801            2 :                 state.dataHVACGlobal->TurnFansOn = false;
    1802              :             } else {
    1803            3 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1804            0 :                     thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1805            0 :                     CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1806            3 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1807            0 :                            thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1808            0 :                     CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1809            3 :                 } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1810            3 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1811            3 :                     thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1812              :                 }
    1813              :             }
    1814            9 :         } else if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) || std::abs(QZnReq) < SmallLoad) {
    1815              :             // in deadband or very small load: set primary air flow to the minimum
    1816            1 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1817              :             // PIU fan off if reheat is not needed, also reset fan flag if fan should be off
    1818            1 :             if (ReheatRequired) {
    1819            1 :                 state.dataHVACGlobal->TurnFansOn = true;
    1820            1 :                 if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1821            1 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1822            1 :                     thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1823            0 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1824            0 :                     if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1825            0 :                         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
    1826              :                     } else {
    1827            0 :                         thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
    1828              :                     }
    1829            0 :                     thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    1830              :                 }
    1831              :             } else {
    1832            0 :                 thisPIU.SecAirMassFlow = 0.0;
    1833            0 :                 state.dataHVACGlobal->TurnFansOn = false;
    1834            0 :                 thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1835              :             }
    1836            8 :         } else if (QZnReq > SmallLoad) {
    1837              :             // heating
    1838              :             // set primary air flow to the minimum
    1839            7 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1840              :             // determine secondary flow rate
    1841            7 :             if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1842            6 :                 thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1843            2 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1844            5 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1845            4 :                        thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1846            4 :                 CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1847            1 :             } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1848            1 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1849            1 :                 thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1850              :             }
    1851              :         } else {
    1852              :             // cooling: set the primary air flow rate to meet the load.
    1853              :             // First calculate the fan temperature rise
    1854            1 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    1855            1 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
    1856            1 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1857              : 
    1858            1 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1859              : 
    1860            1 :             SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
    1861              :             // fan temperature rise [C]
    1862            1 :             Real64 const FanDeltaTemp = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp;
    1863              :             // Assuming the fan is off, calculate the primary air flow needed to meet the zone cooling demand.
    1864            1 :             thisPIU.PriAirMassFlow =
    1865            1 :                 QZnReq /
    1866            1 :                 (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1867            1 :             thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1868              :             // check for fan on or off
    1869            1 :             if ((thisPIU.PriAirMassFlow > thisPIU.FanOnAirMassFlow) && !ReheatRequired) {
    1870            0 :                 thisPIU.SecAirMassFlow = 0.0; // Fan is off unless reheat is required; no secondary air; also reset fan flag
    1871            0 :                 state.dataHVACGlobal->TurnFansOn = false;
    1872              :             } else {
    1873              :                 // fan is on; recalc primary air flow
    1874            1 :                 thisPIU.PriAirMassFlow =
    1875            2 :                     (QZnReq - CpAirZn * thisPIU.SecAirMassFlow *
    1876            1 :                                   (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp + FanDeltaTemp - state.dataLoopNodes->Node(ZoneNode).Temp)) /
    1877            1 :                     (CpAirZn *
    1878            1 :                      min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1879            1 :                 thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1880            1 :                 thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1881              :             }
    1882            1 :             if (QZnReq < 0) {
    1883            1 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1884            0 :                     if (thisPIU.PriAirMassFlow == PriAirMassFlowMax) {
    1885            0 :                         thisPIU.coolingOperatingMode = CoolOpModeType::CoolSecondStage;
    1886              :                     } else {
    1887            0 :                         thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
    1888              :                     }
    1889              :                 } else {
    1890            1 :                     thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
    1891              :                 }
    1892              :             }
    1893              :         }
    1894              :     } else {
    1895              :         // unit is off; no flow
    1896            0 :         thisPIU.PriAirMassFlow = 0.0;
    1897            0 :         thisPIU.SecAirMassFlow = 0.0;
    1898              :     }
    1899              :     // set inlet node flowrates
    1900           14 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
    1901           14 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
    1902           14 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.SecAirMassFlow;
    1903           14 :     if (PriAirMassFlowMax == 0) {
    1904            5 :         thisPIU.PriDamperPosition = 0;
    1905              :     } else {
    1906            9 :         thisPIU.PriDamperPosition = thisPIU.PriAirMassFlow / PriAirMassFlowMax;
    1907              :     }
    1908              : 
    1909              :     // now that inlet airflows have been set, the terminal box components can be simulated.
    1910              :     // fire the fan
    1911           14 :     if (thisPIU.fanType == HVAC::FanType::SystemModel) {
    1912            6 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1913              :             // calculate fan speed ratio
    1914            6 :             Real64 fanFlowRatio(1.0);
    1915            6 :             if (thisPIU.MaxSecAirMassFlow > 0.0) {
    1916            6 :                 fanFlowRatio = thisPIU.SecAirMassFlow / thisPIU.MaxSecAirMassFlow;
    1917              :             }
    1918            6 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
    1919              :         } else {
    1920            0 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1921              :         }
    1922            8 :     } else if (thisPIU.fanType == HVAC::FanType::Constant) {
    1923            8 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1924              :     }
    1925              : 
    1926              :     // fire the mixer
    1927           14 :     SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    1928              : 
    1929              :     // the heating load seen by the reheat coil [W]
    1930           14 :     Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
    1931           14 :                                                (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
    1932              : 
    1933              :     // check if heating coil is off
    1934           14 :     if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleCool) ||
    1935           28 :          (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
    1936            9 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1937            8 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1938              :     }
    1939              : 
    1940              :     // determine what is required of heater for current operating stage
    1941           14 :     if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
    1942            8 :         QCoilReq = 0.0;
    1943            6 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
    1944            1 :         QCoilReq = 0.0;
    1945            5 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
    1946            0 :         QCoilReq = QActualHeating;
    1947            5 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
    1948            1 :         QCoilReq = QActualHeating;
    1949            4 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
    1950            1 :         QCoilReq = QActualHeating;
    1951            3 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
    1952              :         // find heater power to deliver design discharge air temperature
    1953            1 :         Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1954              :         Real64 mixAirEnthalpy =
    1955            1 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1956            1 :         QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
    1957            2 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
    1958              :         // find heater power to deliver maximum discharge air temperature
    1959            2 :         Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1960              :         Real64 mixAirEnthalpy =
    1961            2 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1962            2 :         Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
    1963            2 :         if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
    1964            1 :             QCoilReq = QcoilLimit;
    1965              :         } else {
    1966            1 :             QCoilReq = QActualHeating;
    1967              :         }
    1968              :     } else {
    1969            0 :         ShowSevereError(state, "Incorrect parallel PIU heating operation.");
    1970            0 :         ShowFatalError(state, format("Parallel PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    1971              :     }
    1972           14 :     if ((QCoilReq < SmallLoad) &&
    1973            9 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1974            8 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1975            8 :         QCoilReq = 0.0;
    1976              :     }
    1977              : 
    1978              :     // fire the heating coil
    1979           14 :     switch (thisPIU.HCoilType) {
    1980            0 :     case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
    1981            0 :         if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
    1982              :             // call the reheat coil with the NO FLOW condition
    1983            0 :             Real64 mdot = 0.0;
    1984            0 :             SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
    1985            0 :             SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
    1986            0 :         } else {
    1987              :             // control water flow to obtain output matching QZnReq
    1988            0 :             ControlCompOutput(state,
    1989            0 :                               thisPIU.HCoil,
    1990            0 :                               thisPIU.UnitType,
    1991            0 :                               thisPIU.HCoil_Index,
    1992              :                               FirstHVACIteration,
    1993              :                               QCoilReq,
    1994              :                               thisPIU.HotControlNode,
    1995              :                               MaxWaterFlow,
    1996              :                               MinWaterFlow,
    1997              :                               thisPIU.HotControlOffset,
    1998            0 :                               thisPIU.ControlCompTypeNum,
    1999            0 :                               thisPIU.CompErrIndex,
    2000            0 :                               thisPIU.HCoilInAirNode,
    2001            0 :                               thisPIU.OutAirNode,
    2002              :                               _,
    2003              :                               _,
    2004              :                               _,
    2005            0 :                               thisPIU.HWplantLoc);
    2006              :         }
    2007            0 :         break;
    2008              :     }
    2009            0 :     case HtgCoilType::SteamAirHeating: { // COIL:STEAM:AIRHEATING
    2010            0 :         SimulateSteamCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index, QCoilReq);
    2011            0 :         break;
    2012              :     }
    2013           14 :     case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
    2014           14 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    2015           14 :         break;
    2016              :     }
    2017            0 :     case HtgCoilType::Gas: { // COIL:GAS:HEATING
    2018            0 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    2019            0 :         break;
    2020              :     }
    2021            0 :     default:
    2022            0 :         break;
    2023              :     }
    2024              :     // Power supplied
    2025           14 :     Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
    2026           14 :                       (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
    2027           14 :                        PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
    2028           14 :     thisPIU.HeatingRate = max(0.0, PowerMet);
    2029           14 :     thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
    2030           14 :     thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
    2031           14 :     thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2032           14 :     thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
    2033           14 :     thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
    2034           14 :     if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
    2035            3 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2036            3 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
    2037              :     }
    2038           14 :     if (thisPIU.InducesPlenumAir) {
    2039            0 :         state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2040              :     } else {
    2041           14 :         state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
    2042              :     }
    2043           14 :     state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
    2044           14 :     state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
    2045              : 
    2046           14 :     ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
    2047           14 : }
    2048              : 
    2049        15946 : void ReportCurOperatingControlStage(EnergyPlusData &state, int const piuNum, bool const unitOn, HeatOpModeType heaterMode, CoolOpModeType coolingMode)
    2050              : {
    2051        15946 :     int constexpr undetermined = -1;
    2052        15946 :     int constexpr off = 0;
    2053        15946 :     int constexpr constantVolumeCooling = 1;
    2054        15946 :     int constexpr constantVolumeHeating = 2;
    2055        15946 :     int constexpr deadband = 3;
    2056        15946 :     int constexpr variableSpeedFirstStageCooling = 4;
    2057        15946 :     int constexpr variableSpeedSecondStageCooling = 5;
    2058        15946 :     int constexpr variableSpeedStagedHeatFirstStageHeating = 6;
    2059        15946 :     int constexpr variableSpeedStagedHeatSecondStageHeating = 7;
    2060        15946 :     int constexpr variableSpeedModulatedHeatFirstStageHeating = 8;
    2061        15946 :     int constexpr variableSpeedModulatedHeatSecondStageHeating = 9;
    2062        15946 :     int constexpr variableSpeedModulatedHeatThirdStageHeating = 10;
    2063              : 
    2064        15946 :     state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = undetermined;
    2065              : 
    2066        15946 :     if (!unitOn) {
    2067            1 :         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = off;
    2068              :     } else {
    2069        15945 :         if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::ConstantSpeedFan) {
    2070        15931 :             if (heaterMode != HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
    2071         6802 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeHeating;
    2072         9129 :             } else if (coolingMode != CoolOpModeType::CoolerOff && heaterMode == HeatOpModeType::HeaterOff) {
    2073         7392 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeCooling;
    2074              :             } else {
    2075         1737 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
    2076              :             }
    2077              :         }
    2078        15945 :         if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::VariableSpeedFan) {
    2079           14 :             if (heaterMode != HeatOpModeType::HeaterOff) {
    2080           12 :                 if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    2081            4 :                     if (heaterMode == HeatOpModeType::StagedHeatFirstStage) {
    2082            2 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatFirstStageHeating;
    2083            2 :                     } else if (heaterMode == HeatOpModeType::StagedHeatSecondStage) {
    2084            2 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatSecondStageHeating;
    2085              :                     }
    2086            8 :                 } else if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    2087            8 :                     if (heaterMode == HeatOpModeType::ModulatedHeatFirstStage) {
    2088            2 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatFirstStageHeating;
    2089            6 :                     } else if (heaterMode == HeatOpModeType::ModulatedHeatSecondStage) {
    2090            2 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatSecondStageHeating;
    2091            4 :                     } else if (heaterMode == HeatOpModeType::ModulatedHeatThirdStage) {
    2092            4 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatThirdStageHeating;
    2093              :                     }
    2094              :                 }
    2095            2 :             } else if (coolingMode == CoolOpModeType::CoolFirstStage) {
    2096            1 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedFirstStageCooling;
    2097            1 :             } else if (coolingMode == CoolOpModeType::CoolSecondStage) {
    2098            1 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedSecondStageCooling;
    2099            0 :             } else if (heaterMode == HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
    2100            0 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
    2101              :             }
    2102              :         }
    2103              :     }
    2104        15946 : }
    2105              : 
    2106            2 : void CalcVariableSpeedPIUCoolingBehavior(EnergyPlusData &state,
    2107              :                                          int const piuNum,   // number of the current PIU being simulated
    2108              :                                          int const zoneNode, // zone node number
    2109              :                                          Real64 const zoneLoad,
    2110              :                                          Real64 const loadToHeatSetPt,
    2111              :                                          Real64 const priAirMassFlowMin,
    2112              :                                          [[maybe_unused]] Real64 const priAirMassFlowMax)
    2113              : {
    2114            2 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2115            2 :     thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
    2116              : 
    2117              :     // set min primary flow and low secondary
    2118            2 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = priAirMassFlowMin;
    2119            2 :     Real64 TotAirMassFlow = thisPIU.MinTotAirMassFlow;
    2120            2 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - priAirMassFlowMin);
    2121              : 
    2122              :     // calculate cooling provided to zone at minimum fan speed and minimum primary air mass flow
    2123            2 :     Real64 qdotDelivMinPrim = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2124              : 
    2125            2 :     if (qdotDelivMinPrim <= zoneLoad) { // will provide more cooling than required at minimum primary flow
    2126            0 :         thisPIU.PriAirMassFlow = priAirMassFlowMin;
    2127            0 :         if (qdotDelivMinPrim >=
    2128              :             loadToHeatSetPt) { // will provide more cooling than required but not enough to drop below the heating thermostat setpoint
    2129            0 :             thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.PriAirMassFlow);
    2130            0 :             thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2131              :         } else {
    2132            0 :             if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    2133            0 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, piuNum, zoneNode, loadToHeatSetPt, true, thisPIU.PriAirMassFlow);
    2134            0 :             } else if (thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    2135            0 :                 CalcVariableSpeedPIUModulatedHeatingBehavior(state, piuNum, zoneNode, loadToHeatSetPt, true, thisPIU.PriAirMassFlow);
    2136              :             }
    2137              :         }
    2138              :     } else {
    2139              :         // check how much cooling provided at max fan and primary air
    2140            2 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
    2141            2 :         TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
    2142            2 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - thisPIU.MaxPriAirMassFlow);
    2143            2 :         Real64 qdotDelivMaxFan = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, 1.0);
    2144              : 
    2145            2 :         if (zoneLoad <= qdotDelivMaxFan) { // not going to make it just run at max
    2146            1 :             thisPIU.PriAirMassFlow = thisPIU.PriAirMassFlow;
    2147            1 :             TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
    2148            1 :             thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
    2149            1 :             thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2150            1 :             thisPIU.coolingOperatingMode = CoolOpModeType::CoolSecondStage;
    2151              :         } else {
    2152              :             // call regula falsi solver, vary a coooling control signal for fan speed and primary air flow together from min to max.
    2153            1 :             int constexpr MaxIte(500);    // Maximum number of iterations
    2154            1 :             Real64 constexpr Acc(0.0001); // Accuracy of result
    2155            1 :             int SolFla(0);                // Flag of solver
    2156            1 :             Real64 coolSignal = 0.5;      // starting value
    2157            7 :             auto f = [&state, piuNum, zoneLoad, zoneNode](Real64 const coolSignal) {
    2158            7 :                 return CalcVariableSpeedPIUCoolingResidual(state, coolSignal, piuNum, zoneLoad, zoneNode);
    2159            1 :             };
    2160              : 
    2161            1 :             General::SolveRoot(state, Acc, MaxIte, SolFla, coolSignal, f, 0.0, 1.0);
    2162              : 
    2163            1 :             if (SolFla == -1) {
    2164            0 :                 ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box cooling signal");
    2165            0 :                 ShowContinueErrorTimeStamp(state, "");
    2166            0 :                 ShowFatalError(state, format("Series PIU control failed for {}:{} ", thisPIU.UnitType, thisPIU.Name));
    2167            1 :             } else if (SolFla == -2) {
    2168            0 :                 ShowSevereError(state, "Bad starting values for in calculating variable speed fan powered box cooling signal");
    2169            0 :                 ShowContinueError(state, format("Zone Load to Cooling Setpoint = {:.2R} [W]", zoneLoad));
    2170            0 :                 ShowContinueError(state, format("Load Delivered to Zone at Minimum Fan Speed  = {:.2R} [W]", qdotDelivMinPrim));
    2171            0 :                 ShowContinueErrorTimeStamp(state, "");
    2172            0 :                 ShowFatalError(state, format("Series PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    2173              :             } else {
    2174            1 :                 thisPIU.PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
    2175            1 :                 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
    2176            1 :                 thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
    2177            1 :                 thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2178            1 :                 thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
    2179              :             }
    2180              :         }
    2181              :     }
    2182            2 : }
    2183              : 
    2184            4 : void CalcVariableSpeedPIUStagedHeatingBehavior(EnergyPlusData &state,
    2185              :                                                int const piuNum,   // number of the current PIU being simulated
    2186              :                                                int const zoneNode, // zone node number
    2187              :                                                Real64 const zoneLoad,
    2188              :                                                bool const pri,
    2189              :                                                Real64 const primaryAirMassFlow)
    2190              : {
    2191            4 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2192              : 
    2193              :     // Calculate heating provided to zone with no coil at the maximum secondary flow rate: "1st stage, max fan"
    2194            4 :     if (pri) {
    2195            4 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2196            2 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2197            2 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2198            2 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2199            2 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2200            2 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2201              :         }
    2202              :     } else {
    2203            0 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2204            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2205            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    2206            0 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2207            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2208            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2209              :         }
    2210              :     }
    2211              :     Real64 TotAirMassFlow =
    2212            4 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2213            4 :     Real64 qdotDelivered1stStageMaxFan = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, 1.0);
    2214              : 
    2215              :     // Calculate heating provided to zone with no coil at the minimum secondary flow rate: "1st stage, min fan"
    2216            4 :     if (pri) {
    2217            4 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2218            2 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2219            2 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2220            2 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2221            2 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2222            2 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2223              :         }
    2224              :     } else {
    2225            0 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2226            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2227            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
    2228            0 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2229            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2230            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2231              :         }
    2232              :     }
    2233            4 :     TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2234              :     Real64 qdotDelivered1stStageMinFan =
    2235            4 :         CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2236              : 
    2237            4 :     if (qdotDelivered1stStageMinFan <= zoneLoad && qdotDelivered1stStageMaxFan >= zoneLoad) { // 1st of heating (no coil) can meet the load
    2238              :         // Find fan speed/flow that meets the load through iteration
    2239            2 :         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
    2240            2 :         int constexpr MaxIte(500);   // Maximum number of iterations
    2241            2 :         Real64 constexpr Acc(0.001); // Accuracy of result
    2242            2 :         int SolFla(0);               // Flag of solver
    2243            2 :         Real64 fanSignal = 0.0;
    2244            2 :         fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // average speed as the initial value
    2245           10 :         auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
    2246           10 :             return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, false, fanSignal);
    2247            2 :         };
    2248              : 
    2249            2 :         General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
    2250              : 
    2251            2 :         if (SolFla == -1) {
    2252            0 :             ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box 1st stage heating fan speed");
    2253            0 :             ShowContinueErrorTimeStamp(state, "");
    2254            0 :             ShowFatalError(state, format("PIU control failed for {}:{} ", thisPIU.UnitType, thisPIU.Name));
    2255            2 :         } else if (SolFla == -2) {
    2256            0 :             ShowSevereError(state, "Bad starting values in calculating variable speed fan powered box 1st stage heating fan speed");
    2257            0 :             ShowContinueErrorTimeStamp(state, "");
    2258            0 :             ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    2259              :         } else {
    2260            2 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2261            1 :                 thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2262            1 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2263            1 :                 thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
    2264              :             }
    2265              :         }
    2266            4 :     } else if (qdotDelivered1stStageMaxFan < zoneLoad) {
    2267            2 :         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatSecondStage;
    2268            2 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2269            1 :             thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2270            1 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2271            1 :             thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    2272              :         }
    2273            0 :     } else if (qdotDelivered1stStageMinFan > zoneLoad) {
    2274            0 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2275            0 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2276            0 :             thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2277            0 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2278            0 :             thisPIU.SecAirMassFlow = 0.0;
    2279              :         }
    2280              :     }
    2281            4 : }
    2282              : 
    2283           49 : Real64 CalcVariableSpeedPIUQdotDelivered(EnergyPlusData &state,
    2284              :                                          int const piuNum,   // number of the current PIU being simulated
    2285              :                                          int const zoneNode, // zone node number
    2286              :                                          bool const useDAT,
    2287              :                                          Real64 const totAirMassFlow,
    2288              :                                          Real64 const fanTurnDown)
    2289              : {
    2290           49 :     Real64 qdotDelivered = 0.0;
    2291           49 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2292           49 :     if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2293           30 :         MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    2294           30 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
    2295              :     } else {
    2296           19 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
    2297           19 :         MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    2298              :     }
    2299           49 :     Real64 zoneEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(zoneNode).Temp, state.dataLoopNodes->Node(zoneNode).HumRat);
    2300           49 :     Real64 piuTemp = 0.0;
    2301           49 :     if (useDAT) {
    2302           20 :         piuTemp = thisPIU.designHeatingDAT;
    2303              :     } else {
    2304           29 :         piuTemp = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp;
    2305              :     }
    2306           49 :     Real64 piuEnthalpy = Psychrometrics::PsyHFnTdbW(piuTemp, state.dataLoopNodes->Node(zoneNode).HumRat);
    2307           49 :     qdotDelivered = totAirMassFlow * (piuEnthalpy - zoneEnthalpy);
    2308           49 :     return qdotDelivered;
    2309              : }
    2310              : 
    2311            8 : void CalcVariableSpeedPIUModulatedHeatingBehavior(EnergyPlusData &state,
    2312              :                                                   int const piuNum,   // number of the current PIU being simulated
    2313              :                                                   int const zoneNode, // zone node number
    2314              :                                                   Real64 const zoneLoad,
    2315              :                                                   bool const pri,
    2316              :                                                   Real64 const primaryAirMassFlow)
    2317              : {
    2318            8 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2319              : 
    2320              :     // Calculate heating provided to zone with no coil at the minimum secondary flow rate: "1st stage, min fan"
    2321            8 :     if (pri) {
    2322            8 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2323            4 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2324            4 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2325            4 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2326            4 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2327            4 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2328              :         }
    2329              :     } else {
    2330            0 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2331            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2332            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
    2333            0 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2334            0 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2335            0 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2336              :         }
    2337              :     }
    2338              :     Real64 TotAirMassFlow =
    2339            8 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2340            8 :     Real64 qdotDeliveredEnd1stStage = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2341            8 :     if (qdotDeliveredEnd1stStage >= zoneLoad) { // 1st stage, find heating power at minimum fan speed
    2342            2 :         thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
    2343            2 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2344            1 :             thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    2345            1 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2346            1 :             thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    2347              :         }
    2348              :     } else {
    2349            6 :         if (pri) {
    2350            6 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2351            3 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2352            3 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2353            3 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2354            3 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2355            3 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2356              :             }
    2357              :         } else {
    2358            0 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2359            0 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2360            0 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    2361            0 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2362            0 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2363            0 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2364              :             }
    2365              :         }
    2366            6 :         TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2367              :         Real64 qdotDeliveredEnd2ndStage =
    2368            6 :             CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2369            6 :         if (qdotDeliveredEnd2ndStage > zoneLoad) { // 2nd stage
    2370            2 :             thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatSecondStage;
    2371              :             // Find fan speed that meets zone heating load
    2372            2 :             int constexpr MaxIte(500);                                                                  // Maximum number of iterations
    2373            2 :             Real64 constexpr Acc(0.0001);                                                               // Accuracy of result
    2374            2 :             int SolFla(0);                                                                              // Flag of solver
    2375            2 :             Real64 fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // starting value in middle
    2376            6 :             auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
    2377            6 :                 return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, true, fanSignal);
    2378            2 :             };
    2379              : 
    2380            2 :             General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
    2381              : 
    2382            2 :             if (SolFla == -1) {
    2383            0 :                 ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box 2nd stage heating fan speed");
    2384            0 :                 ShowContinueErrorTimeStamp(state, "");
    2385            0 :                 ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    2386            2 :             } else if (SolFla == -2) {
    2387            0 :                 ShowSevereError(state, "Bad starting values for in calculating variable speed fan powered box 2nd stage heating fan speed");
    2388            0 :                 ShowContinueErrorTimeStamp(state, "");
    2389            0 :                 ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
    2390              :             } else {
    2391            2 :                 if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2392            1 :                     thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2393            1 :                 } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2394            1 :                     thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
    2395              :                 }
    2396              :             }
    2397              :         } else { // 3rd stage, full fan speed
    2398            4 :             thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatThirdStage;
    2399            4 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2400            2 :                 thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
    2401            2 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2402            2 :                 thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    2403              :             }
    2404              :         }
    2405              :     }
    2406            8 : }
    2407              : 
    2408           16 : Real64 CalcVariableSpeedPIUHeatingResidual(EnergyPlusData &state,
    2409              :                                            Real64 const fanSignal,
    2410              :                                            int const piuNum,
    2411              :                                            Real64 const targetQznReq,
    2412              :                                            int const zoneNodeNum,
    2413              :                                            Real64 const primaryMassFlow,
    2414              :                                            bool useDAT,
    2415              :                                            Real64 const fanTurnDown)
    2416              : 
    2417              : {
    2418              :     // used to find a fan speed to meet load to heating setpoint with no heater power
    2419              :     // 1st stage heating for staged heat, also used for undershoot case where cooling at min primary flow would push below heating
    2420              :     // setpoint.
    2421           16 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2422           16 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = primaryMassFlow;
    2423           16 :     Real64 TotAirMassFlow = 0.0;
    2424           16 :     if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2425            8 :         TotAirMassFlow = fanSignal * thisPIU.MaxTotAirMassFlow;
    2426            8 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - primaryMassFlow);
    2427              :     } else {
    2428              :         // parallel
    2429            8 :         TotAirMassFlow = fanSignal * thisPIU.MaxSecAirMassFlow + primaryMassFlow;
    2430            8 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = fanSignal * thisPIU.MaxSecAirMassFlow;
    2431              :     }
    2432              : 
    2433              :     // calculate heating provided to zone
    2434           16 :     Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, useDAT, TotAirMassFlow, fanTurnDown);
    2435              :     // formulate residual and return
    2436           16 :     Real64 Residuum = (targetQznReq - qdotDelivered);
    2437           16 :     return Residuum;
    2438              : }
    2439              : 
    2440            7 : Real64 CalcVariableSpeedPIUCoolingResidual(EnergyPlusData &state, Real64 const coolSignal, int piuNum, Real64 targetQznReq, int zoneNodeNum)
    2441              : {
    2442              :     // used for cooling control with VS fan.  Simultaneous control of fan speed and primary air damper
    2443              :     // given trial cooling signal, calculate the cooling provided and a residual that compares what is delivered vs what the zone
    2444              :     // needs. set the flows, controller acts on fan and damper simultaneously
    2445            7 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2446            7 :     Real64 PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
    2447            7 :     Real64 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
    2448            7 :     Real64 SecAirMassFlow = max(0.0, TotAirMassFlow - PriAirMassFlow);
    2449            7 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = PriAirMassFlow;
    2450            7 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = SecAirMassFlow;
    2451              : 
    2452            7 :     Real64 fanTurnDown = coolSignal * (1.0 - thisPIU.MinFanTurnDownRatio) + thisPIU.MinFanTurnDownRatio;
    2453            7 :     Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, false, TotAirMassFlow, fanTurnDown);
    2454              :     // formulate residual and return
    2455            7 :     Real64 Residuum = (targetQznReq - qdotDelivered);
    2456            7 :     return Residuum;
    2457              : }
    2458              : 
    2459        15916 : void ReportPIU(EnergyPlusData &state, int const PIUNum) // number of the current fan coil unit being simulated
    2460              : {
    2461              : 
    2462              :     // SUBROUTINE INFORMATION:
    2463              :     //       AUTHOR         Fred Buhl
    2464              :     //       DATE WRITTEN   August 2000
    2465              :     //       MODIFIED       na
    2466              :     //       RE-ENGINEERED  na
    2467              : 
    2468              :     // PURPOSE OF THIS SUBROUTINE:
    2469              :     // Fills some report variables for the PIU terminal boxes
    2470              : 
    2471              :     // Using/Aliasing
    2472        15916 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    2473              : 
    2474        15916 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    2475        15916 :     thisPIU.HeatingEnergy = thisPIU.HeatingRate * TimeStepSysSec;
    2476        15916 :     thisPIU.SensCoolEnergy = thisPIU.SensCoolRate * TimeStepSysSec;
    2477              : 
    2478              :     // set zone OA Volume flow rate
    2479        15916 :     thisPIU.CalcOutdoorAirVolumeFlowRate(state);
    2480        15916 : }
    2481              : 
    2482              : // ===================== Utilities =====================================
    2483              : 
    2484            6 : bool PIUnitHasMixer(EnergyPlusData &state, std::string_view CompName) // component (mixer) name
    2485              : {
    2486              : 
    2487              :     // FUNCTION INFORMATION:
    2488              :     //       AUTHOR         Linda Lawrie
    2489              :     //       DATE WRITTEN   September 2011
    2490              :     //       MODIFIED       na
    2491              :     //       RE-ENGINEERED  na
    2492              : 
    2493              :     // PURPOSE OF THIS FUNCTION:
    2494              :     // Given a mixer name, this routine determines if that mixer is found on
    2495              :     // PIUnits.
    2496              : 
    2497              :     // Return value
    2498            6 :     bool YesNo = false; // True if found
    2499              : 
    2500            6 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
    2501            0 :         GetPIUs(state);
    2502            0 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
    2503              :     }
    2504              : 
    2505            6 :     if (state.dataPowerInductionUnits->NumPIUs > 0) {
    2506            6 :         int const ItemNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU, &PowIndUnitData::MixerName);
    2507            6 :         if (ItemNum > 0) {
    2508            6 :             YesNo = true;
    2509              :         }
    2510              :     }
    2511              : 
    2512            6 :     return YesNo;
    2513              : }
    2514              : 
    2515            6 : void PIUInducesPlenumAir(EnergyPlusData &state, int const NodeNum, int const plenumNum) // induced air node number
    2516              : {
    2517              : 
    2518              :     // SUBROUTINE INFORMATION:
    2519              :     //       AUTHOR         Fred Buhl
    2520              :     //       DATE WRITTEN   January 2012
    2521              :     //       MODIFIED       na
    2522              :     //       RE-ENGINEERED  na
    2523              : 
    2524              :     // PURPOSE OF THIS FUNCTION:
    2525              :     // Marks a PIU air terminal unit as obtaining its induced air from
    2526              :     // a plenum.
    2527              : 
    2528            6 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
    2529            3 :         GetPIUs(state);
    2530            3 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
    2531              :     }
    2532              : 
    2533            6 :     for (int PIUIndex = 1; PIUIndex <= state.dataPowerInductionUnits->NumPIUs; ++PIUIndex) {
    2534            1 :         if (NodeNum == state.dataPowerInductionUnits->PIU(PIUIndex).SecAirInNode) {
    2535            1 :             state.dataPowerInductionUnits->PIU(PIUIndex).InducesPlenumAir = true;
    2536            1 :             state.dataPowerInductionUnits->PIU(PIUIndex).plenumIndex = plenumNum;
    2537            1 :             break;
    2538              :         }
    2539              :     }
    2540            6 : }
    2541              : 
    2542        15916 : void PowIndUnitData::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
    2543              : {
    2544              :     // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction
    2545        15916 :     if (this->AirLoopNum > 0) {
    2546        15909 :         this->OutdoorAirFlowRate = (state.dataLoopNodes->Node(this->PriAirInNode).MassFlowRate / state.dataEnvrn->StdRhoAir) *
    2547        15909 :                                    state.dataAirLoop->AirLoopFlow(this->AirLoopNum).OAFrac;
    2548              :     } else {
    2549            7 :         this->OutdoorAirFlowRate = 0.0;
    2550              :     }
    2551        15916 : }
    2552              : 
    2553            7 : void PowIndUnitData::reportTerminalUnit(EnergyPlusData &state)
    2554              : {
    2555              :     // populate the predefined equipment summary report related to air terminals
    2556            7 :     auto &orp = state.dataOutRptPredefined;
    2557            7 :     auto &adu = state.dataDefineEquipment->AirDistUnit(this->ADUNum);
    2558            7 :     if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
    2559            7 :         auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
    2560            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
    2561            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
    2562            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
    2563            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
    2564            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
    2565            7 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
    2566              :     }
    2567            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, this->UnitType);
    2568            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->MaxPriAirVolFlow);
    2569            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, this->MaxSecAirVolFlow);
    2570            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
    2571            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
    2572            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, "n/a");
    2573            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, HCoilNamesUC[(int)this->HCoilType]);
    2574            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "n/a");
    2575            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, HVAC::fanTypeNames[(int)this->fanType]);
    2576            7 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, this->FanName);
    2577            7 : }
    2578              : 
    2579              : } // namespace EnergyPlus::PoweredInductionUnits
        

Generated by: LCOV version 2.0-1