LCOV - code coverage report
Current view: top level - EnergyPlus - PoweredInductionUnits.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 77.1 % 1360 1048
Test Date: 2025-06-02 07:23:51 Functions: 95.2 % 21 20

            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       308345 : 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       308345 :     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       308345 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
     143           14 :         GetPIUs(state);
     144           14 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
     145              :     }
     146              : 
     147              :     // Get the powered induction unit index
     148       308345 :     if (CompIndex == 0) {
     149           57 :         PIUNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU);
     150           57 :         if (PIUNum == 0) {
     151            0 :             ShowFatalError(state, format("SimPIU: PIU Unit not found={}", CompName));
     152              :         }
     153           57 :         CompIndex = PIUNum;
     154              :     } else {
     155       308288 :         PIUNum = CompIndex;
     156       308288 :         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       308288 :         if (state.dataPowerInductionUnits->CheckEquipName(PIUNum)) {
     164           57 :             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           57 :             state.dataPowerInductionUnits->CheckEquipName(PIUNum) = false;
     172              :         }
     173              :     }
     174              : 
     175       616690 :     state.dataSize->CurTermUnitSizingNum =
     176       308345 :         state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(PIUNum).ADUNum).TermUnitSizingNum;
     177              :     // initialize the unit
     178       308345 :     InitPIU(state, PIUNum, FirstHVACIteration);
     179              : 
     180       308345 :     state.dataSize->TermUnitPIU = true;
     181              : 
     182              :     // Select the correct unit type
     183       308345 :     switch (state.dataPowerInductionUnits->PIU(PIUNum).UnitType_Num) {
     184              : 
     185       286787 :     case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat: { //  'AirTerminal:SingleDuct:SeriesPIU:Reheat'
     186              : 
     187       286787 :         CalcSeriesPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
     188       286787 :         break;
     189              :     }
     190        21558 :     case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat: { // 'AirTerminal:SingleDuct:ParallelPIU:Reheat'
     191              : 
     192        21558 :         CalcParallelPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
     193        21558 :         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       308345 :     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       308345 :     ReportPIU(state, PIUNum);
     209       308345 : }
     210              : 
     211           18 : 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           18 :     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           18 :     bool SteamMessageNeeded = true;
     241              : 
     242              :     // find the number of each type of fan coil unit
     243           36 :     state.dataPowerInductionUnits->NumSeriesPIUs =
     244           18 :         state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:SeriesPIU:Reheat");
     245           36 :     state.dataPowerInductionUnits->NumParallelPIUs =
     246           18 :         state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat");
     247           18 :     state.dataPowerInductionUnits->NumPIUs = state.dataPowerInductionUnits->NumSeriesPIUs + state.dataPowerInductionUnits->NumParallelPIUs;
     248              : 
     249           18 :     if (state.dataPowerInductionUnits->NumPIUs > 0) {
     250              :         // GetZonePlenumInput might call this routine before the AirDistUnit has been populated
     251           14 :         if (state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag) {
     252            0 :             ZoneAirLoopEquipmentManager::GetZoneAirLoopEquipment(state);
     253            0 :             state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag = false;
     254              :         }
     255              :     }
     256              : 
     257              :     // allocate the data structures
     258           18 :     state.dataPowerInductionUnits->PIU.allocate(state.dataPowerInductionUnits->NumPIUs);
     259           18 :     state.dataPowerInductionUnits->PiuUniqueNames.reserve(static_cast<unsigned>(state.dataPowerInductionUnits->NumPIUs));
     260           18 :     state.dataPowerInductionUnits->CheckEquipName.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     261              : 
     262           18 :     int PIUNum = 0;
     263           18 :     auto &ip = state.dataInputProcessing->inputProcessor;
     264              :     // loop over Series PIUs; get and load the input data
     265           90 :     for (const std::string cCurrentModuleObject : {"AirTerminal:SingleDuct:SeriesPIU:Reheat", "AirTerminal:SingleDuct:ParallelPIU:Reheat"}) {
     266           36 :         auto const &objectSchemaProps = ip->getObjectSchemaProps(state, cCurrentModuleObject);
     267           36 :         auto const &PIUsInstances = ip->epJSON.find(cCurrentModuleObject);
     268           36 :         if (PIUsInstances != ip->epJSON.end()) {
     269           18 :             auto &PIUInstances = PIUsInstances.value();
     270           75 :             for (auto instance = PIUInstances.begin(); instance != PIUInstances.end(); ++instance) {
     271           57 :                 ++PIUNum;
     272           57 :                 auto const &fields = instance.value();
     273              : 
     274           57 :                 ErrorObjectHeader eoh{routineName, cCurrentModuleObject, instance.key()};
     275              : 
     276           57 :                 GlobalNames::VerifyUniqueInterObjectName(
     277           57 :                     state, state.dataPowerInductionUnits->PiuUniqueNames, Util::makeUPPER(instance.key()), cCurrentModuleObject, "Name", ErrorsFound);
     278           57 :                 auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
     279           57 :                 thisPIU.Name = Util::makeUPPER(instance.key());
     280           57 :                 thisPIU.UnitType = cCurrentModuleObject;
     281           57 :                 ip->markObjectAsUsed(cCurrentModuleObject, instance.key());
     282           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     283           51 :                     thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat;
     284            6 :                 } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     285            6 :                     thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat;
     286              :                 }
     287              : 
     288          114 :                 std::string schedName = ip->getAlphaFieldValue(fields, objectSchemaProps, "availability_schedule_name");
     289           57 :                 if (schedName.empty()) {
     290            4 :                     thisPIU.availSched = Sched::GetScheduleAlwaysOn(state);
     291           53 :                 } 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           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     298          153 :                     thisPIU.MaxTotAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_air_flow_rate");
     299              :                 }
     300           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     301           18 :                     thisPIU.MaxSecAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_secondary_air_flow_rate");
     302              :                 }
     303          114 :                 thisPIU.MaxPriAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_primary_air_flow_rate");
     304          114 :                 thisPIU.MinPriAirFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_primary_air_flow_fraction");
     305           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     306           18 :                     thisPIU.FanOnFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "fan_on_flow_fraction");
     307              :                 }
     308           57 :                 thisPIU.HCoilType = static_cast<HtgCoilType>(
     309          114 :                     getEnumValue(HCoilNamesUC, Util::makeUPPER(ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"))));
     310           57 :                 switch (thisPIU.HCoilType) {
     311           56 :                 case HtgCoilType::SimpleHeating: {
     312           56 :                     thisPIU.HCoil_PlantType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
     313           56 :                     break;
     314              :                 }
     315            1 :                 case HtgCoilType::Electric:
     316              :                 case HtgCoilType::Gas: {
     317            1 :                     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           57 :                 auto connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctSeriesPIUReheat;
     340           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     341            6 :                     connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctParallelPIUReheat;
     342              :                 }
     343          114 :                 thisPIU.PriAirInNode = GetOnlySingleNode(state,
     344          114 :                                                          ip->getAlphaFieldValue(fields, objectSchemaProps, "supply_air_inlet_node_name"),
     345              :                                                          ErrorsFound,
     346              :                                                          connectionType,
     347           57 :                                                          thisPIU.Name,
     348              :                                                          DataLoopNode::NodeFluidType::Air,
     349              :                                                          DataLoopNode::ConnectionType::Inlet,
     350              :                                                          NodeInputManager::CompFluidStream::Primary,
     351              :                                                          ObjectIsParent,
     352              :                                                          "Supply Air Inlet Node Name");
     353              : 
     354          114 :                 thisPIU.SecAirInNode = GetOnlySingleNode(state,
     355          114 :                                                          ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
     356              :                                                          ErrorsFound,
     357              :                                                          connectionType,
     358           57 :                                                          thisPIU.Name,
     359              :                                                          DataLoopNode::NodeFluidType::Air,
     360              :                                                          DataLoopNode::ConnectionType::Inlet,
     361              :                                                          NodeInputManager::CompFluidStream::Primary,
     362              :                                                          ObjectIsParent,
     363              :                                                          "Secondary Air Inlet Node Name");
     364              : 
     365          114 :                 thisPIU.OutAirNode = GetOnlySingleNode(state,
     366          114 :                                                        ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"),
     367              :                                                        ErrorsFound,
     368              :                                                        connectionType,
     369           57 :                                                        thisPIU.Name,
     370              :                                                        DataLoopNode::NodeFluidType::Air,
     371              :                                                        DataLoopNode::ConnectionType::Outlet,
     372              :                                                        NodeInputManager::CompFluidStream::Primary,
     373              :                                                        ObjectIsParent,
     374              :                                                        "Outlet Node Name");
     375              : 
     376          114 :                 thisPIU.HCoilInAirNode = GetOnlySingleNode(state,
     377          114 :                                                            ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
     378              :                                                            ErrorsFound,
     379              :                                                            connectionType,
     380           57 :                                                            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           57 :                 if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
     389           56 :                     thisPIU.HotControlNode = GetCoilWaterInletNode(state,
     390          168 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
     391          224 :                                                                    ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
     392              :                                                                    ErrorsFound);
     393              :                 }
     394           57 :                 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          114 :                 thisPIU.MixerName = ip->getAlphaFieldValue(fields, objectSchemaProps, "zone_mixer_name");
     401          114 :                 thisPIU.FanName = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_name");
     402              : 
     403              :                 // find fan type
     404              :                 // test if Fan:SystemModel fan of this name exists
     405           57 :                 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           57 :                     auto *fan = state.dataFans->fans(thisPIU.Fan_Index);
     411           57 :                     thisPIU.fanType = fan->type;
     412           57 :                     thisPIU.fanAvailSched = fan->availSched;
     413              :                 }
     414              : 
     415          114 :                 thisPIU.HCoil = ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name");
     416           57 :                 bool IsNotOK = false;
     417          114 :                 ValidateComponent(
     418          171 :                     state, HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], thisPIU.HCoil, IsNotOK, cCurrentModuleObject + " - Heating Coil");
     419           57 :                 if (IsNotOK) {
     420            0 :                     ShowContinueError(state, format("In {} = {}", cCurrentModuleObject, thisPIU.Name));
     421            0 :                     ErrorsFound = true;
     422              :                 }
     423          114 :                 thisPIU.MaxVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_hot_water_or_steam_flow_rate");
     424          114 :                 thisPIU.MinVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_hot_water_or_steam_flow_rate");
     425          114 :                 thisPIU.HotControlOffset = ip->getRealFieldValue(fields, objectSchemaProps, "convergence_tolerance");
     426              :                 // Set default convergence tolerance
     427           57 :                 if (thisPIU.HotControlOffset <= 0.0) {
     428            0 :                     thisPIU.HotControlOffset = 0.001;
     429              :                 }
     430              : 
     431              :                 // Variable speed fan inputs
     432          114 :                 std::string fan_control_type = "ConstantSpeed";
     433          114 :                 fan_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_control_type");
     434           57 :                 thisPIU.fanControlType = FanCntrlType::ConstantSpeedFan;
     435           57 :                 if (Util::SameString(fan_control_type, "VariableSpeed")) {
     436            4 :                     thisPIU.fanControlType = FanCntrlType::VariableSpeedFan;
     437            4 :                     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           53 :                 } else if (Util::SameString(fan_control_type, "ConstantSpeed")) {
     443           53 :                     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          171 :                 std::string const heating_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "heating_control_type");
     451           57 :                 thisPIU.heatingControlType = HeatCntrlBehaviorType::Invalid;
     452           57 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
     453            4 :                     if (Util::SameString(heating_control_type, "Staged")) {
     454            2 :                         thisPIU.heatingControlType = HeatCntrlBehaviorType::StagedHeaterBehavior;
     455            2 :                     } else if (Util::SameString(heating_control_type, "Modulated")) {
     456            2 :                         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          114 :                 thisPIU.MinFanTurnDownRatio = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_fan_turn_down_ratio");
     465          114 :                 thisPIU.designHeatingDAT = ip->getRealFieldValue(fields, objectSchemaProps, "design_heating_discharge_air_temperature");
     466          114 :                 thisPIU.highLimitDAT = ip->getRealFieldValue(fields, objectSchemaProps, "high_limit_heating_discharge_air_temperature");
     467              : 
     468              :                 // Add fan to component sets array
     469           57 :                 if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     470          102 :                     SetUpCompSets(state,
     471              :                                   thisPIU.UnitType,
     472              :                                   thisPIU.Name,
     473              :                                   "UNDEFINED",
     474              :                                   thisPIU.FanName,
     475              :                                   "UNDEFINED",
     476          204 :                                   ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"));
     477            6 :                 } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
     478           12 :                     SetUpCompSets(state,
     479              :                                   thisPIU.UnitType,
     480              :                                   thisPIU.Name,
     481              :                                   "UNDEFINED",
     482              :                                   thisPIU.FanName,
     483           24 :                                   ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
     484              :                                   "UNDEFINED");
     485              :                 }
     486              : 
     487              :                 // Add reheat coil to component sets array
     488          285 :                 SetUpCompSets(state,
     489              :                               thisPIU.UnitType,
     490              :                               thisPIU.Name,
     491          171 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
     492          171 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
     493          171 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
     494          228 :                               ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"));
     495              : 
     496              :                 // Register component set data
     497          114 :                 TestCompSet(state,
     498              :                             thisPIU.UnitType,
     499              :                             thisPIU.Name,
     500           57 :                             state.dataLoopNodes->NodeID(thisPIU.PriAirInNode),
     501           57 :                             state.dataLoopNodes->NodeID(thisPIU.OutAirNode),
     502              :                             "Air Nodes");
     503              : 
     504          499 :                 for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
     505          442 :                     if (thisPIU.OutAirNode == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
     506           57 :                         thisPIU.ADUNum = ADUNum;
     507           57 :                         state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisPIU.PriAirInNode;
     508              :                     }
     509              :                 }
     510              :                 // one assumes if there isn't one assigned, it's an error?
     511           57 :                 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           57 :                     bool AirNodeFound = false;
     519              :                     // Fill the Zone Equipment data with the supply air inlet node number of this unit.
     520          580 :                     for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
     521          523 :                         if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
     522           81 :                             continue;
     523              :                         }
     524         1037 :                         for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++SupAirIn) {
     525          652 :                             if (thisPIU.OutAirNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(SupAirIn)) {
     526           57 :                                 state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).InNode = thisPIU.PriAirInNode;
     527           57 :                                 state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).OutNode = thisPIU.OutAirNode;
     528           57 :                                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).TermUnitSizingNum =
     529           57 :                                     state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
     530           57 :                                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).ZoneEqNum = CtrlZone;
     531           57 :                                 AirNodeFound = true;
     532           57 :                                 thisPIU.CtrlZoneNum = CtrlZone; // fill index for later use in finding air loop index
     533           57 :                                 thisPIU.ctrlZoneInNodeIndex = SupAirIn;
     534           57 :                                 break;
     535              :                             }
     536              :                         }
     537              :                     }
     538           57 :                     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           75 :             }
     546              :         }
     547           36 :     }
     548              : 
     549           18 :     if (ErrorsFound) {
     550            0 :         ShowFatalError(state, format("{} Errors found in getting input.  Preceding conditions cause termination.", RoutineName));
     551              :     }
     552              : 
     553           75 :     for (int PIURpt = 1; PIURpt <= state.dataPowerInductionUnits->NumPIUs; ++PIURpt) {
     554           57 :         auto &thisPIU = state.dataPowerInductionUnits->PIU(PIURpt);
     555              : 
     556              :         // Setup Report variables for the PIUs
     557          114 :         SetupOutputVariable(state,
     558              :                             "Zone Air Terminal Primary Damper Position",
     559              :                             Constant::Units::None,
     560           57 :                             thisPIU.PriDamperPosition,
     561              :                             OutputProcessor::TimeStepType::System,
     562              :                             OutputProcessor::StoreType::Average,
     563           57 :                             thisPIU.Name);
     564          114 :         SetupOutputVariable(state,
     565              :                             "Zone Air Terminal Heating Rate",
     566              :                             Constant::Units::W,
     567           57 :                             thisPIU.HeatingRate,
     568              :                             OutputProcessor::TimeStepType::System,
     569              :                             OutputProcessor::StoreType::Average,
     570           57 :                             thisPIU.Name);
     571          114 :         SetupOutputVariable(state,
     572              :                             "Zone Air Terminal Heating Energy",
     573              :                             Constant::Units::J,
     574           57 :                             thisPIU.HeatingEnergy,
     575              :                             OutputProcessor::TimeStepType::System,
     576              :                             OutputProcessor::StoreType::Sum,
     577           57 :                             thisPIU.Name);
     578          114 :         SetupOutputVariable(state,
     579              :                             "Zone Air Terminal Sensible Cooling Rate",
     580              :                             Constant::Units::W,
     581           57 :                             thisPIU.SensCoolRate,
     582              :                             OutputProcessor::TimeStepType::System,
     583              :                             OutputProcessor::StoreType::Average,
     584           57 :                             thisPIU.Name);
     585          114 :         SetupOutputVariable(state,
     586              :                             "Zone Air Terminal Sensible Cooling Energy",
     587              :                             Constant::Units::J,
     588           57 :                             thisPIU.SensCoolEnergy,
     589              :                             OutputProcessor::TimeStepType::System,
     590              :                             OutputProcessor::StoreType::Sum,
     591           57 :                             thisPIU.Name);
     592          114 :         SetupOutputVariable(state,
     593              :                             "Zone Air Terminal Outdoor Air Volume Flow Rate",
     594              :                             Constant::Units::m3_s,
     595           57 :                             thisPIU.OutdoorAirFlowRate,
     596              :                             OutputProcessor::TimeStepType::System,
     597              :                             OutputProcessor::StoreType::Average,
     598           57 :                             thisPIU.Name);
     599          114 :         SetupOutputVariable(state,
     600              :                             "Zone Air Terminal Total Air Mass Flow Rate",
     601              :                             Constant::Units::kg_s,
     602           57 :                             thisPIU.TotMassFlowRate,
     603              :                             OutputProcessor::TimeStepType::System,
     604              :                             OutputProcessor::StoreType::Average,
     605           57 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     606          114 :         SetupOutputVariable(state,
     607              :                             "Zone Air Terminal Primary Air Mass Flow Rate",
     608              :                             Constant::Units::kg_s,
     609           57 :                             thisPIU.PriMassFlowRate,
     610              :                             OutputProcessor::TimeStepType::System,
     611              :                             OutputProcessor::StoreType::Average,
     612           57 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     613          114 :         SetupOutputVariable(state,
     614              :                             "Zone Air Terminal Secondary Air Mass Flow Rate",
     615              :                             Constant::Units::kg_s,
     616           57 :                             thisPIU.SecMassFlowRate,
     617              :                             OutputProcessor::TimeStepType::System,
     618              :                             OutputProcessor::StoreType::Average,
     619           57 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     620          114 :         SetupOutputVariable(state,
     621              :                             "Zone Air Terminal Outlet Discharge Air Temperature",
     622              :                             Constant::Units::C,
     623           57 :                             thisPIU.DischargeAirTemp,
     624              :                             OutputProcessor::TimeStepType::System,
     625              :                             OutputProcessor::StoreType::Average,
     626           57 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     627           57 :         SetupOutputVariable(state,
     628              :                             "Zone Air Terminal Current Operation Control Stage",
     629              :                             Constant::Units::unknown,
     630           57 :                             thisPIU.CurOperationControlStage,
     631              :                             OutputProcessor::TimeStepType::System,
     632              :                             OutputProcessor::StoreType::Average,
     633           57 :                             state.dataPowerInductionUnits->PIU(PIURpt).Name);
     634              :     }
     635           18 : }
     636              : 
     637       308345 : 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       308345 :     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       308345 :     if (state.dataPowerInductionUnits->MyOneTimeFlag) {
     668           14 :         state.dataPowerInductionUnits->MyEnvrnFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     669           14 :         state.dataPowerInductionUnits->MySizeFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     670           14 :         state.dataPowerInductionUnits->MyPlantScanFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
     671           14 :         state.dataPowerInductionUnits->MyOneTimeFlag = false;
     672              :     }
     673              : 
     674       308345 :     if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && allocated(state.dataPlnt->PlantLoop)) {
     675           57 :         if ((thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
     676            1 :             (thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
     677           56 :             bool errFlag = false;
     678           56 :             ScanPlantLoopsForObject(state, thisPIU.HCoil, thisPIU.HCoil_PlantType, thisPIU.HWplantLoc, errFlag, _, _, _, _, _);
     679           56 :             if (errFlag) {
     680            0 :                 ShowFatalError(state, "InitPIU: Program terminated due to previous condition(s).");
     681              :             }
     682           56 :             thisPIU.HotCoilOutNodeNum = DataPlant::CompData::getPlantComponent(state, thisPIU.HWplantLoc).NodeNumOut;
     683              :         }
     684           57 :         state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
     685       308288 :     } else if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && !state.dataGlobal->AnyPlantInModel) {
     686            0 :         state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
     687              :     }
     688              : 
     689       308345 :     if (!state.dataPowerInductionUnits->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
     690           14 :         state.dataPowerInductionUnits->ZoneEquipmentListChecked = true;
     691              :         // Check to see if there is a Air Distribution Unit on the Zone Equipment List
     692           71 :         for (int Loop = 1; Loop <= state.dataPowerInductionUnits->NumPIUs; ++Loop) {
     693           57 :             if (state.dataPowerInductionUnits->PIU(Loop).ADUNum == 0) {
     694            0 :                 continue;
     695              :             }
     696          114 :             if (CheckZoneEquipmentList(state,
     697              :                                        "ZoneHVAC:AirDistributionUnit",
     698           57 :                                        state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(Loop).ADUNum).Name)) {
     699           57 :                 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       308402 :     if (!state.dataGlobal->SysSizingCalc && state.dataPowerInductionUnits->MySizeFlag(PIUNum) &&
     712           57 :         !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
     713              : 
     714           57 :         SizePIU(state, PIUNum);
     715              : 
     716              :         // If there's a hot water control node number defined in PIU
     717           57 :         if (thisPIU.HotControlNode > 0) {
     718              :             // plant upgrade note? why no separate handling of steam coil? add it ?
     719              :             // local plant fluid density
     720           56 :             Real64 const rho = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     721              : 
     722           56 :             thisPIU.MaxHotWaterFlow = rho * thisPIU.MaxVolHotWaterFlow;
     723           56 :             thisPIU.MinHotWaterFlow = rho * thisPIU.MinVolHotWaterFlow;
     724           56 :             InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
     725              :         }
     726              : 
     727           57 :         state.dataPowerInductionUnits->MySizeFlag(PIUNum) = false;
     728              :     }
     729              : 
     730       308345 :     int const PriNode = thisPIU.PriAirInNode;
     731       308345 :     int const SecNode = thisPIU.SecAirInNode;
     732              : 
     733              :     // Do the Begin Environment initializations
     734       308345 :     if (state.dataGlobal->BeginEnvrnFlag && state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum)) {
     735          321 :         Real64 const RhoAir = state.dataEnvrn->StdRhoAir;
     736          321 :         int const OutletNode = thisPIU.OutAirNode;
     737              :         // set the mass flow rates from the input volume flow rates
     738          321 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     739          287 :             thisPIU.MaxTotAirMassFlow = RhoAir * thisPIU.MaxTotAirVolFlow;
     740          287 :             thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
     741          287 :             thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
     742          287 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     743          287 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
     744          287 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
     745              :         } else {
     746              :             // parallel
     747           34 :             thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
     748           34 :             thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
     749           34 :             thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     750           34 :             thisPIU.FanOnAirMassFlow = RhoAir * thisPIU.FanOnFlowFrac * thisPIU.MaxPriAirVolFlow;
     751           34 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     752           34 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
     753           34 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
     754              :         }
     755          321 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
     756           20 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     757           10 :                 thisPIU.MinTotAirMassFlow = thisPIU.MaxTotAirMassFlow * thisPIU.MinFanTurnDownRatio;
     758           10 :                 thisPIU.MaxSecAirVolFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
     759           10 :                 thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     760           10 :                 thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
     761              :             } else {
     762           10 :                 thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
     763           10 :                 thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MaxSecAirMassFlow * thisPIU.MinFanTurnDownRatio);
     764           10 :                 thisPIU.MinTotAirMassFlow = thisPIU.MinSecAirMassFlow + thisPIU.MinPriAirMassFlow;
     765              :             }
     766              :         }
     767              : 
     768          637 :         if (((thisPIU.HCoilType == HtgCoilType::SimpleHeating) || (thisPIU.HCoilType == HtgCoilType::SteamAirHeating)) &&
     769          316 :             !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
     770          316 :             InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
     771              :         }
     772              : 
     773          321 :         if (thisPIU.AirLoopNum == 0) { // fill air loop index
     774           57 :             if (thisPIU.CtrlZoneNum > 0 && thisPIU.ctrlZoneInNodeIndex > 0) {
     775           57 :                 thisPIU.AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
     776           57 :                 state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).AirLoopNum = thisPIU.AirLoopNum;
     777              :             }
     778              :         }
     779              : 
     780          321 :         state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = false;
     781              :     } // end one time inits
     782              : 
     783       308345 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     784       305515 :         state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = true;
     785              :     }
     786              : 
     787              :     // Do the start of HVAC time step initializations
     788       308345 :     if (FirstHVACIteration) {
     789              :         // check for upstream zero flow. If nonzero and schedule ON, set primary flow to max
     790       143939 :         if (thisPIU.availSched->getCurrentVal() > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRate > 0.0) {
     791       113572 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     792       102830 :                 state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
     793       102830 :                 state.dataLoopNodes->Node(SecNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
     794              :             } else {
     795        10742 :                 state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
     796        10742 :                 state.dataLoopNodes->Node(SecNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
     797              :             }
     798              :         } else {
     799        30367 :             state.dataLoopNodes->Node(PriNode).MassFlowRate = 0.0;
     800        30367 :             state.dataLoopNodes->Node(SecNode).MassFlowRate = 0.0;
     801              :         }
     802              :         // reset the max and min avail flows
     803       143939 :         if (thisPIU.availSched->getCurrentVal() > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail > 0.0) {
     804       113572 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
     805       102830 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
     806       102830 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
     807       102830 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
     808       102830 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
     809              :             } else {
     810        10742 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
     811        10742 :                 state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
     812        10742 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
     813        10742 :                 state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
     814              :             }
     815              :         } else {
     816        30367 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = 0.0;
     817        30367 :             state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = 0.0;
     818        30367 :             state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = 0.0;
     819        30367 :             state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
     820              :         }
     821              :     }
     822              : 
     823              :     // Do the following initializations every time step
     824              : 
     825              :     // None needed
     826       308345 : }
     827              : 
     828           57 : 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           57 :     bool IsAutoSize = false;           // Indicator to autosize
     859           57 :     bool IsMaxPriFlowAutoSize = false; // Indicate if the maximum terminal flow is autosize
     860           57 :     int SysSizNum = 0;                 // System sizing number
     861           57 :     Real64 DesCoilLoad = 0.0;
     862           57 :     bool ErrorsFound = false;
     863              : 
     864           57 :     auto &TermUnitSizing(state.dataSize->TermUnitSizing);
     865           57 :     auto &CurTermUnitSizingNum(state.dataSize->CurTermUnitSizingNum);
     866           57 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
     867              : 
     868           57 :     if (thisPIU.MaxPriAirVolFlow == AutoSize) {
     869           57 :         IsAutoSize = true;
     870              :     }
     871           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     872           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     879              :             // Autosized maximum primary air flow for reporting
     880           57 :             Real64 MaxPriAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     881           57 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     882           57 :             if (MaxPriAirVolFlowDes < SmallAirVolFlow) {
     883            0 :                 MaxPriAirVolFlowDes = 0.0;
     884              :             }
     885              : 
     886           57 :             if (IsAutoSize) {
     887           57 :                 thisPIU.MaxPriAirVolFlow = MaxPriAirVolFlowDes;
     888           57 :                 IsMaxPriFlowAutoSize = true;
     889           57 :                 BaseSizer::reportSizerOutput(
     890              :                     state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Primary Air Flow Rate [m3/s]", MaxPriAirVolFlowDes);
     891              :             } else {
     892            0 :                 if (thisPIU.MaxPriAirVolFlow > 0.0 && MaxPriAirVolFlowDes > 0.0) {
     893              :                     // Hardsized maximum primary air flow for reporting
     894            0 :                     Real64 const MaxPriAirVolFlowUser = thisPIU.MaxPriAirVolFlow;
     895            0 :                     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            0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
     903            0 :                         if ((std::abs(MaxPriAirVolFlowDes - MaxPriAirVolFlowUser) / MaxPriAirVolFlowUser) >
     904            0 :                             state.dataSize->AutoVsHardSizingThreshold) {
     905            0 :                             ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
     906            0 :                             ShowContinueError(state, format("User-Specified Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowUser));
     907            0 :                             ShowContinueError(state, format("differs from Design Size Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowDes));
     908            0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     909            0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     910              :                         }
     911              :                     }
     912              :                 }
     913              :             }
     914              :         }
     915              :     }
     916              : 
     917           57 :     IsAutoSize = false;
     918           57 :     if (thisPIU.MaxTotAirVolFlow == AutoSize) {
     919           51 :         IsAutoSize = true;
     920              :     }
     921           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     922           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     929              :             // Autosized maximum air flow for reporting
     930           57 :             Real64 MaxTotAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     931           57 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     932           57 :             if (MaxTotAirVolFlowDes < SmallAirVolFlow) {
     933            0 :                 MaxTotAirVolFlowDes = 0.0;
     934              :             }
     935           57 :             if (IsAutoSize) {
     936           51 :                 thisPIU.MaxTotAirVolFlow = MaxTotAirVolFlowDes;
     937           51 :                 BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Air Flow Rate [m3/s]", MaxTotAirVolFlowDes);
     938              :             } else {
     939            6 :                 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           57 :     bool SizingDesRunThisAirSys = false;
     966           57 :     if (state.dataSize->SysSizingRunDone) {
     967           57 :         int const AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
     968           57 :         if (AirLoopNum > 0) {
     969           57 :             CheckThisAirSystemForSizing(state, AirLoopNum, SizingDesRunThisAirSys);
     970              :         }
     971              : 
     972              :         // get system sizing id if a sizing run has been done for this system
     973           57 :         if (SizingDesRunThisAirSys) {
     974           57 :             SysSizNum = Util::FindItemInList(
     975           57 :                 state.dataSize->FinalSysSizing(AirLoopNum).AirPriLoopName, state.dataSize->SysSizInput, &SystemSizingInputData::AirPriLoopName);
     976           57 :             if (SysSizNum == 0) {
     977            0 :                 SysSizNum = 1; // use first when none applicable
     978              :             }
     979              :         }
     980              :     }
     981              : 
     982           57 :     IsAutoSize = false;
     983           57 :     if (thisPIU.MaxSecAirVolFlow == AutoSize) {
     984            6 :         IsAutoSize = true;
     985              :     }
     986           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
     987           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
     994              :             // Autosized maximum secondary air flow for reporting
     995           57 :             Real64 MaxSecAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
     996           57 :                                              state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
     997           57 :             if (MaxSecAirVolFlowDes < SmallAirVolFlow) {
     998            0 :                 MaxSecAirVolFlowDes = 0.0;
     999              :             }
    1000           57 :             if (IsAutoSize) {
    1001            6 :                 thisPIU.MaxSecAirVolFlow = MaxSecAirVolFlowDes;
    1002            6 :                 BaseSizer::reportSizerOutput(
    1003              :                     state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Secondary Air Flow Rate [m3/s]", MaxSecAirVolFlowDes);
    1004              :             } else {
    1005           51 :                 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           57 :     IsAutoSize = false;
    1032           57 :     if (thisPIU.MinPriAirFlowFrac == AutoSize) {
    1033           57 :         IsAutoSize = true;
    1034              :     }
    1035           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1036           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1043              :             // Autosized minimum primary air flow fraction for reporting
    1044           57 :             Real64 MinPriAirFlowFracDes = 0.0;
    1045          114 :             if (thisPIU.MaxPriAirVolFlow >= SmallAirVolFlow &&
    1046           57 :                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA >= SmallAirVolFlow) {
    1047           57 :                 MinPriAirFlowFracDes = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA / thisPIU.MaxPriAirVolFlow;
    1048              :             }
    1049           57 :             if (SizingDesRunThisAirSys) {
    1050           57 :                 if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) { // 62.1 simplified procedure
    1051            2 :                     if (thisPIU.MaxPriAirVolFlow > 0.0) {
    1052            2 :                         MinPriAirFlowFracDes = 1.5 *
    1053            2 :                                                max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozClgByZone,
    1054            2 :                                                    state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozHtgByZone) /
    1055            2 :                                                thisPIU.MaxPriAirVolFlow;
    1056              : 
    1057              :                         // adjust maximum flow rate
    1058            2 :                         if (MinPriAirFlowFracDes > 1.0 && IsMaxPriFlowAutoSize) {
    1059            0 :                             thisPIU.MaxPriAirVolFlow *= MinPriAirFlowFracDes;
    1060            0 :                             MinPriAirFlowFracDes = 1.0;
    1061            0 :                             ShowWarningError(state,
    1062            0 :                                              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            0 :                                                     thisPIU.Name));
    1065            2 :                         } else if (MinPriAirFlowFracDes > 1.0) {
    1066            0 :                             ShowWarningError(
    1067              :                                 state,
    1068            0 :                                 format("SingleDuctSystem:SizeSys: Maximum primary air flow rate for {} is potentially too low.", thisPIU.Name));
    1069            0 :                             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            0 :                             ShowContinueError(state, format(" User-specified maximum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow));
    1073            0 :                             ShowContinueError(
    1074              :                                 state,
    1075            0 :                                 format(" Calculated minimum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow * MinPriAirFlowFracDes));
    1076            0 :                             MinPriAirFlowFracDes = 1.0;
    1077              :                         }
    1078              :                     }
    1079              :                 }
    1080              :             }
    1081           57 :             if (IsAutoSize) {
    1082           57 :                 if (SizingDesRunThisAirSys) {
    1083           57 :                     if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) {
    1084            2 :                         state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VpzMinByZoneSPSized = true;
    1085              :                     }
    1086              :                 }
    1087           57 :                 thisPIU.MinPriAirFlowFrac = MinPriAirFlowFracDes;
    1088           57 :                 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           57 :     if (CurTermUnitSizingNum > 0) {
    1118              : 
    1119           57 :         if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat) {
    1120           51 :             TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxTotAirVolFlow;
    1121            6 :         } else if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat) {
    1122            6 :             TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxSecAirVolFlow + thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
    1123              :         }
    1124              :     }
    1125              : 
    1126           57 :     IsAutoSize = false;
    1127           57 :     if (thisPIU.FanOnFlowFrac == AutoSize) {
    1128            4 :         IsAutoSize = true;
    1129              :     }
    1130           57 :     if (state.dataSize->CurZoneEqNum > 0) {
    1131           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1137              :             // Autosized fan on flow fraction for reporting
    1138           57 :             Real64 FanOnFlowFracDes = thisPIU.MinPriAirFlowFrac;
    1139           57 :             if (IsAutoSize) {
    1140            4 :                 thisPIU.FanOnFlowFrac = FanOnFlowFracDes;
    1141            4 :                 BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Fan On Flow Fraction", FanOnFlowFracDes);
    1142              :             } else {
    1143           53 :                 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           57 :     IsAutoSize = false;
    1168           57 :     if (thisPIU.MaxVolHotWaterFlow == AutoSize) { //.or.()) THEN
    1169           57 :         IsAutoSize = true;
    1170              :     }
    1171           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1172           57 :         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           57 :             CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
    1179           57 :             if (Util::SameString(HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], "Coil:Heating:Water")) {
    1180              : 
    1181           56 :                 int const CoilWaterInletNode = GetCoilWaterInletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
    1182           56 :                 int const CoilWaterOutletNode = GetCoilWaterOutletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
    1183              : 
    1184              :                 // Autosized maximum hot water flow for reporting
    1185           56 :                 Real64 MaxVolHotWaterFlowDes = 0.0;
    1186              : 
    1187           56 :                 if (IsAutoSize) {
    1188              :                     int const PltSizHeatNum =
    1189           56 :                         MyPlantSizingIndex(state, "Coil:Heating:Water", thisPIU.HCoil, CoilWaterInletNode, CoilWaterOutletNode, ErrorsFound);
    1190           56 :                     if (PltSizHeatNum > 0) {
    1191              : 
    1192           56 :                         if (state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatMassFlow >= SmallAirVolFlow) {
    1193              :                             Real64 const CoilInTemp =
    1194           56 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU * thisPIU.MinPriAirFlowFrac +
    1195           56 :                                 state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).ZoneTempAtHeatPeak * (1.0 - thisPIU.MinPriAirFlowFrac);
    1196           56 :                             Real64 const CoilOutTemp = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesTemp;
    1197           56 :                             Real64 const CoilOutHumRat = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesHumRat;
    1198           56 :                             Real64 const DesMassFlow = state.dataEnvrn->StdRhoAir * TermUnitSizing(CurTermUnitSizingNum).AirVolFlow;
    1199           56 :                             DesCoilLoad = PsyCpAirFnW(CoilOutHumRat) * DesMassFlow * (CoilOutTemp - CoilInTemp);
    1200              : 
    1201           56 :                             Real64 const rho = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum)
    1202           56 :                                                    .glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
    1203           56 :                             Real64 const Cp = state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum)
    1204           56 :                                                   .glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineName);
    1205              : 
    1206           56 :                             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           56 :                     thisPIU.MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
    1216           56 :                     BaseSizer::reportSizerOutput(
    1217              :                         state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Reheat Water Flow Rate [m3/s]", MaxVolHotWaterFlowDes);
    1218          112 :                     BaseSizer::reportSizerOutput(state,
    1219              :                                                  thisPIU.UnitType,
    1220              :                                                  thisPIU.Name,
    1221              :                                                  "Design Size Reheat Coil Inlet Air Temperature [C]",
    1222           56 :                                                  state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU);
    1223          112 :                     BaseSizer::reportSizerOutput(state,
    1224              :                                                  thisPIU.UnitType,
    1225              :                                                  thisPIU.Name,
    1226              :                                                  "Design Size Reheat Coil Inlet Air Humidity Ratio [kgWater/kgDryAir]",
    1227           56 :                                                  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            1 :                 thisPIU.MaxVolHotWaterFlow = 0.0;
    1256              :             }
    1257              :         }
    1258              :     }
    1259              : 
    1260           57 :     IsAutoSize = false;
    1261           57 :     if (thisPIU.MaxVolHotSteamFlow == AutoSize) {
    1262            0 :         IsAutoSize = true;
    1263              :     }
    1264           57 :     if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
    1265           57 :         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           57 :             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           57 :                 thisPIU.MaxVolHotSteamFlow = 0.0;
    1335              :             }
    1336              :         }
    1337              :     }
    1338              : 
    1339           57 :     if (CurTermUnitSizingNum > 0) {
    1340           57 :         TermUnitSizing(CurTermUnitSizingNum).MinPriFlowFrac = thisPIU.MinPriAirFlowFrac;
    1341           57 :         TermUnitSizing(CurTermUnitSizingNum).plenumIndex = thisPIU.plenumIndex;
    1342           57 :         TermUnitSizing(CurTermUnitSizingNum).MaxHWVolFlow = thisPIU.MaxVolHotWaterFlow;
    1343           57 :         TermUnitSizing(CurTermUnitSizingNum).MaxSTVolFlow = thisPIU.MaxVolHotSteamFlow;
    1344           57 :         TermUnitSizing(CurTermUnitSizingNum).DesHeatingLoad = DesCoilLoad; // coil report
    1345           57 :         TermUnitSizing(CurTermUnitSizingNum).InducesPlenumAir = thisPIU.InducesPlenumAir;
    1346           57 :         if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
    1347           56 :             SetCoilDesFlow(state,
    1348           56 :                            HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)],
    1349           56 :                            thisPIU.HCoil,
    1350           56 :                            TermUnitSizing(CurTermUnitSizingNum).AirVolFlow,
    1351              :                            ErrorsFound);
    1352              :         }
    1353              :     }
    1354              : 
    1355           57 :     if (ErrorsFound) {
    1356            0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
    1357              :     }
    1358           57 : }
    1359              : 
    1360       286787 : 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       286787 :     bool UnitOn(true); // TRUE if unit is on
    1415       286787 :     bool PriOn(true);  // TRUE if primary air available
    1416              : 
    1417       286787 :     Real64 QCoilReq = 0.0;     // required heating coil outlet to meet zone load
    1418       286787 :     Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
    1419       286787 :     Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
    1420              : 
    1421              :     // initialize local variables
    1422       286787 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    1423              : 
    1424       286787 :     Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
    1425       286787 :     Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
    1426              :     Real64 const QZnReq =
    1427       286787 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
    1428              :     Real64 const QToHeatSetPt =
    1429       286787 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W]  remaining load to heating setpoint
    1430       286787 :     Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);          // zone air specific heat [J/kg-C]
    1431       286787 :     thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;   // primary air mass flow rate [kg/s]
    1432       286787 :     thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;   // secondary air mass flow rate [kg/s]
    1433       286787 :     if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1434         7378 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1435              :     } else {
    1436       279409 :         thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1437              :     }
    1438       286787 :     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       286787 :     if (thisPIU.HotControlNode > 0) {
    1443       276068 :         if (FirstHVACIteration) {
    1444       128319 :             MaxWaterFlow = thisPIU.MaxHotWaterFlow;
    1445       128319 :             MinWaterFlow = thisPIU.MinHotWaterFlow;
    1446              :         } else {
    1447       147749 :             MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
    1448       147749 :             MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
    1449              :         }
    1450              :     }
    1451       286787 :     if (thisPIU.availSched->getCurrentVal() <= 0.0) {
    1452           14 :         UnitOn = false;
    1453              :     }
    1454       286787 :     if ((thisPIU.fanAvailSched->getCurrentVal() <= 0.0 || state.dataHVACGlobal->TurnFansOff) && !state.dataHVACGlobal->TurnFansOn) {
    1455        56933 :         UnitOn = false;
    1456              :     }
    1457       286787 :     if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
    1458        61517 :         PriOn = false;
    1459              :     }
    1460              :     // Set the mass flow rates
    1461       286787 :     if (UnitOn) {
    1462              :         // unit is on
    1463       229854 :         if (!PriOn) {
    1464              :             // no primary air flow
    1465         4605 :             thisPIU.PriAirMassFlow = 0.0;
    1466              :             // PIU fan off if there is no heating load, also reset fan flag if fan should be off
    1467         4605 :             if (QZnReq <= SmallLoad) {
    1468         2127 :                 thisPIU.SecAirMassFlow = 0.0;
    1469         2127 :                 state.dataHVACGlobal->TurnFansOn = false;
    1470              :             } else {
    1471         2478 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1472            6 :                     thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1473            3 :                     CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1474         2475 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1475            3 :                            thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1476            3 :                     CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1477         2472 :                 } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1478         2472 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1479         2472 :                     thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow;
    1480              :                 }
    1481              :             }
    1482       225249 :         } 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        44018 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1485        44018 :             if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1486        41160 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1487        41160 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1488         2858 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1489         2858 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.PriAirMassFlow);
    1490              :             }
    1491       181231 :         } else if (QZnReq > SmallLoad) {
    1492              :             // heating: set primary air flow to the minimum
    1493       115852 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1494              :             // determine secondary flow rate
    1495       115852 :             if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1496         2718 :                 thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1497         1360 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1498       114492 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1499         1358 :                        thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1500         1358 :                 CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1501       113134 :             } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1502       113134 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1503       113134 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1504              :             }
    1505              :         } else {
    1506        65379 :             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        63587 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1511        63587 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    1512        63587 :                 SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
    1513        63587 :                 state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1514              : 
    1515              :                 // fan temperature rise [C]
    1516              :                 Real64 const FanDeltaTemp =
    1517        63587 :                     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        63587 :                 Real64 const OutletTempNeeded = state.dataLoopNodes->Node(ZoneNode).Temp + QZnReq / (thisPIU.MaxTotAirMassFlow * CpAirZn);
    1521              : 
    1522              :                 // mixer outlet temperature needed to meet cooling load
    1523        63587 :                 Real64 const MixTempNeeded = OutletTempNeeded - FanDeltaTemp;
    1524              : 
    1525        63587 :                 if (MixTempNeeded <= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp) { //
    1526        19952 :                     thisPIU.PriAirMassFlow = PriAirMassFlowMax;
    1527        87270 :                 } else if (MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp &&
    1528        43635 :                            MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp) {
    1529          462 :                     thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1530              :                 } else {
    1531        43173 :                     thisPIU.PriAirMassFlow =
    1532        43173 :                         thisPIU.MaxTotAirMassFlow * (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - MixTempNeeded) /
    1533        43173 :                         max(SmallTempDiff,
    1534        43173 :                             state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp);
    1535        43173 :                     thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1536              :                 }
    1537        63587 :                 thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
    1538        63587 :                 if (QZnReq < 0) {
    1539        63587 :                     thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
    1540              :                 }
    1541         1792 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1542         1792 :                 CalcVariableSpeedPIUCoolingBehavior(state, PIUNum, ZoneNode, QZnReq, QToHeatSetPt, PriAirMassFlowMin, PriAirMassFlowMax);
    1543              :             }
    1544              :         }
    1545              :     } else {
    1546              :         // unit is off ; no flow
    1547        56933 :         thisPIU.PriAirMassFlow = 0.0;
    1548        56933 :         thisPIU.SecAirMassFlow = 0.0;
    1549              :     }
    1550              :     // set inlet node flowrates
    1551       286787 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
    1552       286787 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
    1553       286787 :     if (PriAirMassFlowMax == 0) {
    1554        61517 :         thisPIU.PriDamperPosition = 0;
    1555              :     } else {
    1556       225270 :         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       286787 :     SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    1562              : 
    1563              :     // fire the fan
    1564       286787 :     if (thisPIU.fanType == HVAC::FanType::SystemModel) {
    1565       175105 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1566              :             // calculate fan speed ratio
    1567         7378 :             Real64 fanFlowRatio(1.0);
    1568         7378 :             if (thisPIU.MaxTotAirMassFlow > 0.0) {
    1569         7376 :                 fanFlowRatio = (thisPIU.PriAirMassFlow + thisPIU.SecAirMassFlow) / thisPIU.MaxTotAirMassFlow;
    1570              :             }
    1571         7378 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
    1572              :         } else {
    1573       167727 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1574              :         }
    1575       111682 :     } else if (thisPIU.fanType == HVAC::FanType::Constant) {
    1576       111682 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1577              :     }
    1578              : 
    1579              :     // the heating load seen by the reheat coil [W]
    1580       286787 :     Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
    1581       286787 :                                                (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
    1582              : 
    1583              :     // check if heating coil is off
    1584       229854 :     if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleCool) ||
    1585       573574 :          (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
    1586       168067 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1587       168067 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1588              :     }
    1589              : 
    1590              :     // determine what is required of heater for current operating stage
    1591       286787 :     if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
    1592       168069 :         QCoilReq = 0.0;
    1593       118718 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
    1594            0 :         QCoilReq = 0.0;
    1595       118718 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
    1596       116008 :         QCoilReq = QActualHeating;
    1597         2710 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
    1598         1356 :         QCoilReq = QActualHeating;
    1599         1354 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
    1600         1354 :         QCoilReq = QActualHeating;
    1601            0 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
    1602              :         // find heater power to deliver design discharge air temperature
    1603            0 :         Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1604              :         Real64 mixAirEnthalpy =
    1605            0 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1606            0 :         QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
    1607            0 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
    1608              :         // find heater power to deliver maximum discharge air temperature
    1609            0 :         Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1610              :         Real64 mixAirEnthalpy =
    1611            0 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1612            0 :         Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
    1613            0 :         if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
    1614            0 :             QCoilReq = QcoilLimit;
    1615              :         } else {
    1616            0 :             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       286787 :     if ((QCoilReq < SmallLoad) &&
    1623       168069 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1624       168069 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1625       168069 :         QCoilReq = 0.0;
    1626              :     }
    1627              : 
    1628              :     // fire the heating coil
    1629       286787 :     switch (thisPIU.HCoilType) {
    1630       276068 :     case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
    1631       276068 :         if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
    1632              :             // call the reheat coil with the NO FLOW condition
    1633       161656 :             Real64 mdot = 0.0;
    1634       161656 :             SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
    1635              : 
    1636       161656 :             SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
    1637       161656 :         } else {
    1638              :             // control water flow to obtain output matching QZnReq
    1639       343236 :             ControlCompOutput(state,
    1640       114412 :                               thisPIU.HCoil,
    1641       114412 :                               thisPIU.UnitType,
    1642       114412 :                               thisPIU.HCoil_Index,
    1643              :                               FirstHVACIteration,
    1644              :                               QCoilReq,
    1645              :                               thisPIU.HotControlNode,
    1646              :                               MaxWaterFlow,
    1647              :                               MinWaterFlow,
    1648              :                               thisPIU.HotControlOffset,
    1649       114412 :                               thisPIU.ControlCompTypeNum,
    1650       114412 :                               thisPIU.CompErrIndex,
    1651       114412 :                               thisPIU.HCoilInAirNode,
    1652       114412 :                               thisPIU.OutAirNode,
    1653              :                               _,
    1654              :                               _,
    1655              :                               _,
    1656       114412 :                               thisPIU.HWplantLoc);
    1657              :         }
    1658       276068 :         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        10719 :     case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
    1665        10719 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    1666        10719 :         break;
    1667              :     }
    1668            0 :     case HtgCoilType::Gas: { // COIL:GAS:HEATING
    1669            0 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    1670            0 :         break;
    1671              :     }
    1672            0 :     default:
    1673            0 :         break;
    1674              :     }
    1675              : 
    1676              :     // Power supplied
    1677       286787 :     Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
    1678       286787 :                       (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
    1679       286787 :                        PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
    1680       286787 :     thisPIU.HeatingRate = max(0.0, PowerMet);
    1681       286787 :     thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
    1682       286787 :     thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
    1683       286787 :     thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    1684       286787 :     thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
    1685       286787 :     thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
    1686       286787 :     if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
    1687        59064 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1688        59064 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
    1689              :     }
    1690       286787 :     if (thisPIU.InducesPlenumAir) {
    1691       168076 :         state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    1692              :     } else {
    1693       118711 :         state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
    1694              :     }
    1695       286787 :     state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
    1696       286787 :     state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
    1697              : 
    1698       286787 :     ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
    1699       286787 : }
    1700              : 
    1701        21558 : 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        21558 :     bool UnitOn(true); // TRUE if unit is on
    1745        21558 :     bool PriOn(true);  // TRUE if primary air available
    1746              : 
    1747        21558 :     Real64 QCoilReq = 0.0;     // required heating coil outlet to meet zone load
    1748        21558 :     Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
    1749        21558 :     Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
    1750              : 
    1751              :     // initialize local variables
    1752        21558 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    1753              : 
    1754        21558 :     Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
    1755        21558 :     Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
    1756        21558 :     thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;                 // primary air mass flow rate [kg/s]
    1757        21558 :     thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;                 // secondary air mass flow rate [kg/s]
    1758              :     Real64 const QZnReq =
    1759        21558 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
    1760              :     Real64 const QToHeatSetPt =
    1761        21558 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W]  remaining load to heating setpoint
    1762        21558 :     Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);          // zone air specific heat [J/kg-C]
    1763        21558 :     thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1764        21558 :     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        21558 :     if (thisPIU.HotControlNode > 0) {
    1769        21558 :         if (FirstHVACIteration) {
    1770        10795 :             MaxWaterFlow = thisPIU.MaxHotWaterFlow;
    1771        10795 :             MinWaterFlow = thisPIU.MinHotWaterFlow;
    1772              :         } else {
    1773        10763 :             MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
    1774        10763 :             MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
    1775              :         }
    1776              :     }
    1777        21558 :     if (thisPIU.availSched->getCurrentVal() <= 0.0) {
    1778           14 :         UnitOn = false;
    1779              :     }
    1780        21558 :     if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
    1781          788 :         PriOn = false;
    1782              :     }
    1783              :     // Set the mass flow rates
    1784        21558 :     if (UnitOn) {
    1785              :         // unit is on
    1786              :         // Calculate if reheat is needed
    1787        21544 :         bool ReheatRequired = false;
    1788              :         Real64 const qMinPrimary =
    1789              :             PriAirMassFlowMin *
    1790        21544 :             (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1791        21544 :         if (qMinPrimary < QToHeatSetPt) {
    1792        15009 :             ReheatRequired = true;
    1793              :         }
    1794              : 
    1795        21544 :         if (!PriOn) {
    1796              :             // no primary air flow
    1797          774 :             thisPIU.PriAirMassFlow = 0.0;
    1798              :             // PIU fan off if there is no heating load, also reset fan flag if fan should be off
    1799          774 :             if (QZnReq <= SmallLoad) {
    1800           49 :                 thisPIU.SecAirMassFlow = 0.0;
    1801           49 :                 state.dataHVACGlobal->TurnFansOn = false;
    1802              :             } else {
    1803          725 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1804            6 :                     thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1805            3 :                     CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1806          722 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1807            3 :                            thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1808            3 :                     CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1809          719 :                 } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1810          719 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1811          719 :                     thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1812              :                 }
    1813              :             }
    1814        20770 :         } 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         5141 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1817              :             // PIU fan off if reheat is not needed, also reset fan flag if fan should be off
    1818         5141 :             if (ReheatRequired) {
    1819         4991 :                 state.dataHVACGlobal->TurnFansOn = true;
    1820         4991 :                 if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1821         2493 :                     thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1822         2493 :                     thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1823         2498 :                 } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1824         2498 :                     if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1825         1369 :                         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
    1826              :                     } else {
    1827         1129 :                         thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
    1828              :                     }
    1829         2498 :                     thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    1830              :                 }
    1831              :             } else {
    1832          150 :                 thisPIU.SecAirMassFlow = 0.0;
    1833          150 :                 state.dataHVACGlobal->TurnFansOn = false;
    1834          150 :                 thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1835              :             }
    1836        15629 :         } else if (QZnReq > SmallLoad) {
    1837              :             // heating
    1838              :             // set primary air flow to the minimum
    1839         8215 :             thisPIU.PriAirMassFlow = PriAirMassFlowMin;
    1840              :             // determine secondary flow rate
    1841         8215 :             if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1842         2722 :                 thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    1843         1364 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1844         6851 :             } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
    1845         1358 :                        thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    1846         1358 :                 CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
    1847         5493 :             } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
    1848         5493 :                 thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
    1849         5493 :                 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         7414 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    1855         7414 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
    1856         7414 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    1857              : 
    1858         7414 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1859              : 
    1860         7414 :             SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
    1861              :             // fan temperature rise [C]
    1862         7414 :             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         7414 :             thisPIU.PriAirMassFlow =
    1865         7414 :                 QZnReq /
    1866         7414 :                 (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1867         7414 :             thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1868              :             // check for fan on or off
    1869         7414 :             if ((thisPIU.PriAirMassFlow > thisPIU.FanOnAirMassFlow) && !ReheatRequired) {
    1870         6336 :                 thisPIU.SecAirMassFlow = 0.0; // Fan is off unless reheat is required; no secondary air; also reset fan flag
    1871         6336 :                 state.dataHVACGlobal->TurnFansOn = false;
    1872              :             } else {
    1873              :                 // fan is on; recalc primary air flow
    1874         1078 :                 thisPIU.PriAirMassFlow =
    1875         2156 :                     (QZnReq - CpAirZn * thisPIU.SecAirMassFlow *
    1876         1078 :                                   (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp + FanDeltaTemp - state.dataLoopNodes->Node(ZoneNode).Temp)) /
    1877         1078 :                     (CpAirZn *
    1878         1078 :                      min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
    1879         1078 :                 thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
    1880         1078 :                 thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    1881              :             }
    1882         7414 :             if (QZnReq < 0) {
    1883         7414 :                 if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1884         2092 :                     if (thisPIU.PriAirMassFlow == PriAirMassFlowMax) {
    1885            0 :                         thisPIU.coolingOperatingMode = CoolOpModeType::CoolSecondStage;
    1886              :                     } else {
    1887         2092 :                         thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
    1888              :                     }
    1889              :                 } else {
    1890         5322 :                     thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
    1891              :                 }
    1892              :             }
    1893              :         }
    1894              :     } else {
    1895              :         // unit is off; no flow
    1896           14 :         thisPIU.PriAirMassFlow = 0.0;
    1897           14 :         thisPIU.SecAirMassFlow = 0.0;
    1898              :     }
    1899              :     // set inlet node flowrates
    1900        21558 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
    1901        21558 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
    1902        21558 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.SecAirMassFlow;
    1903        21558 :     if (PriAirMassFlowMax == 0) {
    1904          787 :         thisPIU.PriDamperPosition = 0;
    1905              :     } else {
    1906        20771 :         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        21558 :     if (thisPIU.fanType == HVAC::FanType::SystemModel) {
    1912        14320 :         if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
    1913              :             // calculate fan speed ratio
    1914         7378 :             Real64 fanFlowRatio(1.0);
    1915         7378 :             if (thisPIU.MaxSecAirMassFlow > 0.0) {
    1916         7376 :                 fanFlowRatio = thisPIU.SecAirMassFlow / thisPIU.MaxSecAirMassFlow;
    1917              :             }
    1918         7378 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
    1919              :         } else {
    1920         6942 :             state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1921              :         }
    1922         7238 :     } else if (thisPIU.fanType == HVAC::FanType::Constant) {
    1923         7238 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
    1924              :     }
    1925              : 
    1926              :     // fire the mixer
    1927        21558 :     SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    1928              : 
    1929              :     // the heating load seen by the reheat coil [W]
    1930        21558 :     Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
    1931        21558 :                                                (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
    1932              : 
    1933              :     // check if heating coil is off
    1934        21544 :     if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::SetptType::SingleCool) ||
    1935        43116 :          (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
    1936        12657 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1937        11275 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1938              :     }
    1939              : 
    1940              :     // determine what is required of heater for current operating stage
    1941        21558 :     if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
    1942        11293 :         QCoilReq = 0.0;
    1943        10265 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
    1944         1382 :         QCoilReq = 0.0;
    1945         8883 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
    1946         6199 :         QCoilReq = QActualHeating;
    1947         2684 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
    1948         1330 :         QCoilReq = QActualHeating;
    1949         1354 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
    1950         1354 :         QCoilReq = QActualHeating;
    1951            0 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
    1952              :         // find heater power to deliver design discharge air temperature
    1953            0 :         Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1954              :         Real64 mixAirEnthalpy =
    1955            0 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1956            0 :         QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
    1957            0 :     } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
    1958              :         // find heater power to deliver maximum discharge air temperature
    1959            0 :         Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1960              :         Real64 mixAirEnthalpy =
    1961            0 :             Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
    1962            0 :         Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
    1963            0 :         if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
    1964            0 :             QCoilReq = QcoilLimit;
    1965              :         } else {
    1966            0 :             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        21558 :     if ((QCoilReq < SmallLoad) &&
    1973        12675 :         (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
    1974        11293 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    1975        11293 :         QCoilReq = 0.0;
    1976              :     }
    1977              : 
    1978              :     // fire the heating coil
    1979        21558 :     switch (thisPIU.HCoilType) {
    1980        21558 :     case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
    1981        21558 :         if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
    1982              :             // call the reheat coil with the NO FLOW condition
    1983        12675 :             Real64 mdot = 0.0;
    1984        12675 :             SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
    1985        12675 :             SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
    1986        12675 :         } else {
    1987              :             // control water flow to obtain output matching QZnReq
    1988        26649 :             ControlCompOutput(state,
    1989         8883 :                               thisPIU.HCoil,
    1990         8883 :                               thisPIU.UnitType,
    1991         8883 :                               thisPIU.HCoil_Index,
    1992              :                               FirstHVACIteration,
    1993              :                               QCoilReq,
    1994              :                               thisPIU.HotControlNode,
    1995              :                               MaxWaterFlow,
    1996              :                               MinWaterFlow,
    1997              :                               thisPIU.HotControlOffset,
    1998         8883 :                               thisPIU.ControlCompTypeNum,
    1999         8883 :                               thisPIU.CompErrIndex,
    2000         8883 :                               thisPIU.HCoilInAirNode,
    2001         8883 :                               thisPIU.OutAirNode,
    2002              :                               _,
    2003              :                               _,
    2004              :                               _,
    2005         8883 :                               thisPIU.HWplantLoc);
    2006              :         }
    2007        21558 :         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            0 :     case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
    2014            0 :         SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
    2015            0 :         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        21558 :     Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
    2026        21558 :                       (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
    2027        21558 :                        PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
    2028        21558 :     thisPIU.HeatingRate = max(0.0, PowerMet);
    2029        21558 :     thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
    2030        21558 :     thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
    2031        21558 :     thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2032        21558 :     thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
    2033        21558 :     thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
    2034        21558 :     if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
    2035           74 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2036           74 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
    2037              :     }
    2038        21558 :     if (thisPIU.InducesPlenumAir) {
    2039         7306 :         state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2040              :     } else {
    2041        14252 :         state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
    2042              :     }
    2043        21558 :     state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
    2044        21558 :     state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
    2045              : 
    2046        21558 :     ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
    2047        21558 : }
    2048              : 
    2049       308345 : void ReportCurOperatingControlStage(EnergyPlusData &state, int const piuNum, bool const unitOn, HeatOpModeType heaterMode, CoolOpModeType coolingMode)
    2050              : {
    2051       308345 :     int constexpr undetermined = -1;
    2052       308345 :     int constexpr off = 0;
    2053       308345 :     int constexpr constantVolumeCooling = 1;
    2054       308345 :     int constexpr constantVolumeHeating = 2;
    2055       308345 :     int constexpr deadband = 3;
    2056       308345 :     int constexpr variableSpeedFirstStageCooling = 4;
    2057       308345 :     int constexpr variableSpeedSecondStageCooling = 5;
    2058       308345 :     int constexpr variableSpeedStagedHeatFirstStageHeating = 6;
    2059       308345 :     int constexpr variableSpeedStagedHeatSecondStageHeating = 7;
    2060       308345 :     int constexpr variableSpeedModulatedHeatFirstStageHeating = 8;
    2061       308345 :     int constexpr variableSpeedModulatedHeatSecondStageHeating = 9;
    2062       308345 :     int constexpr variableSpeedModulatedHeatThirdStageHeating = 10;
    2063              : 
    2064       308345 :     state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = undetermined;
    2065              : 
    2066       308345 :     if (!unitOn) {
    2067        56947 :         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = off;
    2068              :     } else {
    2069       251398 :         if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::ConstantSpeedFan) {
    2070       236642 :             if (heaterMode != HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
    2071       122207 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeHeating;
    2072       114435 :             } else if (coolingMode != CoolOpModeType::CoolerOff && heaterMode == HeatOpModeType::HeaterOff) {
    2073        68909 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeCooling;
    2074              :             } else {
    2075        45526 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
    2076              :             }
    2077              :         }
    2078       251398 :         if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::VariableSpeedFan) {
    2079        14756 :             if (heaterMode != HeatOpModeType::HeaterOff) {
    2080         6776 :                 if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    2081         4068 :                     if (heaterMode == HeatOpModeType::StagedHeatFirstStage) {
    2082         1382 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatFirstStageHeating;
    2083         2686 :                     } else if (heaterMode == HeatOpModeType::StagedHeatSecondStage) {
    2084         2686 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatSecondStageHeating;
    2085              :                     }
    2086         2708 :                 } else if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    2087         2708 :                     if (heaterMode == HeatOpModeType::ModulatedHeatFirstStage) {
    2088         2708 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatFirstStageHeating;
    2089            0 :                     } else if (heaterMode == HeatOpModeType::ModulatedHeatSecondStage) {
    2090            0 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatSecondStageHeating;
    2091            0 :                     } else if (heaterMode == HeatOpModeType::ModulatedHeatThirdStage) {
    2092            0 :                         state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatThirdStageHeating;
    2093              :                     }
    2094              :                 }
    2095         7980 :             } else if (coolingMode == CoolOpModeType::CoolFirstStage) {
    2096         3502 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedFirstStageCooling;
    2097         4478 :             } else if (coolingMode == CoolOpModeType::CoolSecondStage) {
    2098            0 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedSecondStageCooling;
    2099         4478 :             } else if (heaterMode == HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
    2100         4478 :                 state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
    2101              :             }
    2102              :         }
    2103              :     }
    2104       308345 : }
    2105              : 
    2106         1792 : 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         1792 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2115         1792 :     thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
    2116              : 
    2117              :     // set min primary flow and low secondary
    2118         1792 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = priAirMassFlowMin;
    2119         1792 :     Real64 TotAirMassFlow = thisPIU.MinTotAirMassFlow;
    2120         1792 :     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         1792 :     Real64 qdotDelivMinPrim = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2124              : 
    2125         1792 :     if (qdotDelivMinPrim <= zoneLoad) { // will provide more cooling than required at minimum primary flow
    2126          382 :         thisPIU.PriAirMassFlow = priAirMassFlowMin;
    2127          382 :         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          382 :             if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
    2133          208 :                 CalcVariableSpeedPIUStagedHeatingBehavior(state, piuNum, zoneNode, loadToHeatSetPt, true, thisPIU.PriAirMassFlow);
    2134          174 :             } else if (thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
    2135          174 :                 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         1410 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
    2141         1410 :         TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
    2142         1410 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - thisPIU.MaxPriAirMassFlow);
    2143         1410 :         Real64 qdotDelivMaxFan = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, 1.0);
    2144              : 
    2145         1410 :         if (zoneLoad <= qdotDelivMaxFan) { // not going to make it just run at max
    2146            0 :             thisPIU.PriAirMassFlow = thisPIU.PriAirMassFlow;
    2147            0 :             TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
    2148            0 :             thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
    2149            0 :             thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2150            0 :             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         1410 :             int constexpr MaxIte(500);    // Maximum number of iterations
    2154         1410 :             Real64 constexpr Acc(0.0001); // Accuracy of result
    2155         1410 :             int SolFla(0);                // Flag of solver
    2156         1410 :             Real64 coolSignal = 0.5;      // starting value
    2157         6977 :             auto f = [&state, piuNum, zoneLoad, zoneNode](Real64 const coolSignal) {
    2158         6977 :                 return CalcVariableSpeedPIUCoolingResidual(state, coolSignal, piuNum, zoneLoad, zoneNode);
    2159         1410 :             };
    2160              : 
    2161         1410 :             General::SolveRoot(state, Acc, MaxIte, SolFla, coolSignal, f, 0.0, 1.0);
    2162              : 
    2163         1410 :             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         1410 :             } 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         1410 :                 thisPIU.PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
    2175         1410 :                 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
    2176         1410 :                 thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
    2177         1410 :                 thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2178         1410 :                 thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
    2179              :             }
    2180              :         }
    2181              :     }
    2182         1792 : }
    2183              : 
    2184         2938 : 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         2938 :     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         2938 :     if (pri) {
    2195         2932 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2196         1568 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2197         1568 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2198         1364 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2199         1364 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2200         1364 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2201              :         }
    2202              :     } else {
    2203            6 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2204            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2205            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    2206            3 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2207            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2208            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2209              :         }
    2210              :     }
    2211              :     Real64 TotAirMassFlow =
    2212         2938 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2213         2938 :     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         2938 :     if (pri) {
    2217         2932 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2218         1568 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2219         1568 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2220         1364 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2221         1364 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2222         1364 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2223              :         }
    2224              :     } else {
    2225            6 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2226            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2227            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
    2228            3 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2229            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2230            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2231              :         }
    2232              :     }
    2233         2938 :     TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2234              :     Real64 qdotDelivered1stStageMinFan =
    2235         2938 :         CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2236              : 
    2237         2938 :     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           13 :         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
    2240           13 :         int constexpr MaxIte(500);   // Maximum number of iterations
    2241           13 :         Real64 constexpr Acc(0.001); // Accuracy of result
    2242           13 :         int SolFla(0);               // Flag of solver
    2243           13 :         Real64 fanSignal = 0.0;
    2244           13 :         fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // average speed as the initial value
    2245           64 :         auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
    2246           64 :             return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, false, fanSignal);
    2247           13 :         };
    2248              : 
    2249           13 :         General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
    2250              : 
    2251           13 :         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           13 :         } 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           13 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2261            0 :                 thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2262           13 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2263           13 :                 thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
    2264              :             }
    2265              :         }
    2266         2938 :     } else if (qdotDelivered1stStageMaxFan < zoneLoad) {
    2267         2900 :         thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatSecondStage;
    2268         2900 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2269         1567 :             thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2270         1333 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2271         1333 :             thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    2272              :         }
    2273           25 :     } else if (qdotDelivered1stStageMinFan > zoneLoad) {
    2274           25 :         thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
    2275           25 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2276            4 :             thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2277           21 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2278           21 :             thisPIU.SecAirMassFlow = 0.0;
    2279              :         }
    2280              :     }
    2281         2938 : }
    2282              : 
    2283        19017 : 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        19017 :     Real64 qdotDelivered = 0.0;
    2291        19017 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2292        19017 :     if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2293        14857 :         MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    2294        14857 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
    2295              :     } else {
    2296         4160 :         state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
    2297         4160 :         MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
    2298              :     }
    2299        19017 :     Real64 zoneEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(zoneNode).Temp, state.dataLoopNodes->Node(zoneNode).HumRat);
    2300        19017 :     Real64 piuTemp = 0.0;
    2301        19017 :     if (useDAT) {
    2302         2898 :         piuTemp = thisPIU.designHeatingDAT;
    2303              :     } else {
    2304        16119 :         piuTemp = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp;
    2305              :     }
    2306        19017 :     Real64 piuEnthalpy = Psychrometrics::PsyHFnTdbW(piuTemp, state.dataLoopNodes->Node(zoneNode).HumRat);
    2307        19017 :     qdotDelivered = totAirMassFlow * (piuEnthalpy - zoneEnthalpy);
    2308        19017 :     return qdotDelivered;
    2309              : }
    2310              : 
    2311         2896 : 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         2896 :     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         2896 :     if (pri) {
    2322         2890 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2323         1532 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2324         1532 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2325         1358 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2326         1358 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2327         1358 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2328              :         }
    2329              :     } else {
    2330            6 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2331            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2332            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
    2333            3 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2334            3 :             state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2335            3 :             state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
    2336              :         }
    2337              :     }
    2338              :     Real64 TotAirMassFlow =
    2339         2896 :         state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2340         2896 :     Real64 qdotDeliveredEnd1stStage = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2341         2896 :     if (qdotDeliveredEnd1stStage >= zoneLoad) { // 1st stage, find heating power at minimum fan speed
    2342         2894 :         thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
    2343         2894 :         if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2344         1534 :             thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    2345         1360 :         } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2346         1360 :             thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
    2347              :         }
    2348              :     } else {
    2349            2 :         if (pri) {
    2350            0 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2351            0 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2352            0 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
    2353            0 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2354            0 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
    2355            0 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2356              :             }
    2357              :         } else {
    2358            2 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2359            1 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2360            1 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
    2361            1 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2362            1 :                 state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
    2363            1 :                 state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
    2364              :             }
    2365              :         }
    2366            2 :         TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
    2367              :         Real64 qdotDeliveredEnd2ndStage =
    2368            2 :             CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
    2369            2 :         if (qdotDeliveredEnd2ndStage > zoneLoad) { // 2nd stage
    2370            0 :             thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatSecondStage;
    2371              :             // Find fan speed that meets zone heating load
    2372            0 :             int constexpr MaxIte(500);                                                                  // Maximum number of iterations
    2373            0 :             Real64 constexpr Acc(0.0001);                                                               // Accuracy of result
    2374            0 :             int SolFla(0);                                                                              // Flag of solver
    2375            0 :             Real64 fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // starting value in middle
    2376            0 :             auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
    2377            0 :                 return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, true, fanSignal);
    2378            0 :             };
    2379              : 
    2380            0 :             General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
    2381              : 
    2382            0 :             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            0 :             } 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            0 :                 if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2392            0 :                     thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
    2393            0 :                 } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2394            0 :                     thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
    2395              :                 }
    2396              :             }
    2397              :         } else { // 3rd stage, full fan speed
    2398            2 :             thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatThirdStage;
    2399            2 :             if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2400            1 :                 thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
    2401            1 :             } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
    2402            1 :                 thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
    2403              :             }
    2404              :         }
    2405              :     }
    2406         2896 : }
    2407              : 
    2408           64 : 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           64 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2422           64 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = primaryMassFlow;
    2423           64 :     Real64 TotAirMassFlow = 0.0;
    2424           64 :     if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
    2425            0 :         TotAirMassFlow = fanSignal * thisPIU.MaxTotAirMassFlow;
    2426            0 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - primaryMassFlow);
    2427              :     } else {
    2428              :         // parallel
    2429           64 :         TotAirMassFlow = fanSignal * thisPIU.MaxSecAirMassFlow + primaryMassFlow;
    2430           64 :         state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = fanSignal * thisPIU.MaxSecAirMassFlow;
    2431              :     }
    2432              : 
    2433              :     // calculate heating provided to zone
    2434           64 :     Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, useDAT, TotAirMassFlow, fanTurnDown);
    2435              :     // formulate residual and return
    2436           64 :     Real64 Residuum = (targetQznReq - qdotDelivered);
    2437           64 :     return Residuum;
    2438              : }
    2439              : 
    2440         6977 : 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         6977 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
    2446         6977 :     Real64 PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
    2447         6977 :     Real64 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
    2448         6977 :     Real64 SecAirMassFlow = max(0.0, TotAirMassFlow - PriAirMassFlow);
    2449         6977 :     state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = PriAirMassFlow;
    2450         6977 :     state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = SecAirMassFlow;
    2451              : 
    2452         6977 :     Real64 fanTurnDown = coolSignal * (1.0 - thisPIU.MinFanTurnDownRatio) + thisPIU.MinFanTurnDownRatio;
    2453         6977 :     Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, false, TotAirMassFlow, fanTurnDown);
    2454              :     // formulate residual and return
    2455         6977 :     Real64 Residuum = (targetQznReq - qdotDelivered);
    2456         6977 :     return Residuum;
    2457              : }
    2458              : 
    2459       308345 : 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       308345 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    2473              : 
    2474       308345 :     auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
    2475       308345 :     thisPIU.HeatingEnergy = thisPIU.HeatingRate * TimeStepSysSec;
    2476       308345 :     thisPIU.SensCoolEnergy = thisPIU.SensCoolRate * TimeStepSysSec;
    2477              : 
    2478              :     // set zone OA Volume flow rate
    2479       308345 :     thisPIU.CalcOutdoorAirVolumeFlowRate(state);
    2480       308345 : }
    2481              : 
    2482              : // ===================== Utilities =====================================
    2483              : 
    2484           63 : 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           63 :     bool YesNo = false; // True if found
    2499              : 
    2500           63 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
    2501            2 :         GetPIUs(state);
    2502            2 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
    2503              :     }
    2504              : 
    2505           63 :     if (state.dataPowerInductionUnits->NumPIUs > 0) {
    2506           57 :         int const ItemNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU, &PowIndUnitData::MixerName);
    2507           57 :         if (ItemNum > 0) {
    2508           57 :             YesNo = true;
    2509              :         }
    2510              :     }
    2511              : 
    2512           63 :     return YesNo;
    2513              : }
    2514              : 
    2515           26 : 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           26 :     if (state.dataPowerInductionUnits->GetPIUInputFlag) {
    2529            2 :         GetPIUs(state);
    2530            2 :         state.dataPowerInductionUnits->GetPIUInputFlag = false;
    2531              :     }
    2532              : 
    2533          137 :     for (int PIUIndex = 1; PIUIndex <= state.dataPowerInductionUnits->NumPIUs; ++PIUIndex) {
    2534          130 :         if (NodeNum == state.dataPowerInductionUnits->PIU(PIUIndex).SecAirInNode) {
    2535           19 :             state.dataPowerInductionUnits->PIU(PIUIndex).InducesPlenumAir = true;
    2536           19 :             state.dataPowerInductionUnits->PIU(PIUIndex).plenumIndex = plenumNum;
    2537           19 :             break;
    2538              :         }
    2539              :     }
    2540           26 : }
    2541              : 
    2542       308345 : void PowIndUnitData::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
    2543              : {
    2544              :     // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction
    2545       308345 :     if (this->AirLoopNum > 0) {
    2546       308288 :         this->OutdoorAirFlowRate = (state.dataLoopNodes->Node(this->PriAirInNode).MassFlowRate / state.dataEnvrn->StdRhoAir) *
    2547       308288 :                                    state.dataAirLoop->AirLoopFlow(this->AirLoopNum).OAFrac;
    2548              :     } else {
    2549           57 :         this->OutdoorAirFlowRate = 0.0;
    2550              :     }
    2551       308345 : }
    2552              : 
    2553           57 : void PowIndUnitData::reportTerminalUnit(EnergyPlusData &state)
    2554              : {
    2555              :     // populate the predefined equipment summary report related to air terminals
    2556           57 :     auto &orp = state.dataOutRptPredefined;
    2557           57 :     auto &adu = state.dataDefineEquipment->AirDistUnit(this->ADUNum);
    2558           57 :     if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
    2559           57 :         auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
    2560           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
    2561           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
    2562           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
    2563           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
    2564           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
    2565           57 :         OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
    2566              :     }
    2567           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, this->UnitType);
    2568           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->MaxPriAirVolFlow);
    2569           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, this->MaxSecAirVolFlow);
    2570           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
    2571           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
    2572           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, "n/a");
    2573           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, HCoilNamesUC[(int)this->HCoilType]);
    2574           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "n/a");
    2575           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, HVAC::fanTypeNames[(int)this->fanType]);
    2576           57 :     OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, this->FanName);
    2577           57 : }
    2578              : 
    2579              : } // namespace EnergyPlus::PoweredInductionUnits
        

Generated by: LCOV version 2.0-1