LCOV - code coverage report
Current view: top level - EnergyPlus - GeneralRoutines.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 753 931 80.9 %
Date: 2023-01-17 19:17:23 Functions: 19 19 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : #include <string>
      51             : 
      52             : // ObjexxFCL Headers
      53             : #include <ObjexxFCL/Array.functions.hh>
      54             : #include <ObjexxFCL/Array1D.hh>
      55             : #include <ObjexxFCL/Array2D.hh>
      56             : #include <ObjexxFCL/Fmath.hh>
      57             : #include <ObjexxFCL/member.functions.hh>
      58             : 
      59             : // EnergyPlus Headers
      60             : #include <EnergyPlus/BaseboardRadiator.hh>
      61             : #include <EnergyPlus/Construction.hh>
      62             : #include <EnergyPlus/ConvectionCoefficients.hh>
      63             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      64             : #include <EnergyPlus/DataAirLoop.hh>
      65             : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
      66             : #include <EnergyPlus/DataEnvironment.hh>
      67             : #include <EnergyPlus/DataHVACGlobals.hh>
      68             : #include <EnergyPlus/DataHeatBalSurface.hh>
      69             : #include <EnergyPlus/DataHeatBalance.hh>
      70             : #include <EnergyPlus/DataLoopNode.hh>
      71             : #include <EnergyPlus/DataSizing.hh>
      72             : #include <EnergyPlus/DataSurfaces.hh>
      73             : #include <EnergyPlus/DataZoneEquipment.hh>
      74             : #include <EnergyPlus/ExhaustAirSystemManager.hh>
      75             : #include <EnergyPlus/FanCoilUnits.hh>
      76             : #include <EnergyPlus/GeneralRoutines.hh>
      77             : #include <EnergyPlus/HVACSingleDuctInduc.hh>
      78             : #include <EnergyPlus/HWBaseboardRadiator.hh>
      79             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      80             : #include <EnergyPlus/Material.hh>
      81             : #include <EnergyPlus/MixerComponent.hh>
      82             : #include <EnergyPlus/OutdoorAirUnit.hh>
      83             : #include <EnergyPlus/PlantUtilities.hh>
      84             : #include <EnergyPlus/PoweredInductionUnits.hh>
      85             : #include <EnergyPlus/Psychrometrics.hh>
      86             : #include <EnergyPlus/PurchasedAirManager.hh>
      87             : #include <EnergyPlus/ScheduleManager.hh>
      88             : #include <EnergyPlus/SolarCollectors.hh>
      89             : #include <EnergyPlus/SplitterComponent.hh>
      90             : #include <EnergyPlus/SteamBaseboardRadiator.hh>
      91             : #include <EnergyPlus/UnitHeater.hh>
      92             : #include <EnergyPlus/UnitVentilator.hh>
      93             : #include <EnergyPlus/UtilityRoutines.hh>
      94             : #include <EnergyPlus/VentilatedSlab.hh>
      95             : #include <EnergyPlus/WaterCoils.hh>
      96             : #include <EnergyPlus/ZonePlenum.hh>
      97             : 
      98             : namespace EnergyPlus {
      99             : 
     100             : // Integer constants for different system types handled by the routines in this file
     101             : enum GeneralRoutinesEquipNums
     102             : {
     103             :     ParallelPIUReheatNum = 1,
     104             :     SeriesPIUReheatNum = 2,
     105             :     HeatingCoilWaterNum = 3,
     106             :     BBWaterConvOnlyNum = 4,
     107             :     BBSteamRadConvNum = 5,
     108             :     BBWaterRadConvNum = 6,
     109             :     FourPipeFanCoilNum = 7,
     110             :     OutdoorAirUnitNum = 8,
     111             :     UnitHeaterNum = 9,
     112             :     UnitVentilatorNum = 10,
     113             :     VentilatedSlabNum = 11
     114             : };
     115             : 
     116             : enum class AirLoopHVACCompType
     117             : {
     118             :     Invalid = -1,
     119             :     SupplyPlenum,
     120             :     ZoneSplitter,
     121             :     ZoneMixer,
     122             :     ReturnPlenum,
     123             :     Num
     124             : };
     125             : 
     126             : constexpr std::array<std::string_view, static_cast<int>(AirLoopHVACCompType::Num)> AirLoopHVACCompTypeNamesUC{
     127             :     "AIRLOOPHVAC:SUPPLYPLENUM", "AIRLOOPHVAC:ZONESPLITTER", "AIRLOOPHVAC:ZONEMIXER", "AIRLOOPHVAC:RETURNPLENUM"};
     128             : 
     129    11202809 : void ControlCompOutput(EnergyPlusData &state,
     130             :                        std::string const &CompName,           // the component Name
     131             :                        std::string const &CompType,           // Type of component
     132             :                        int &CompNum,                          // Index of component in component array
     133             :                        bool const FirstHVACIteration,         // flag for 1st HVAV iteration in the time step
     134             :                        Real64 const QZnReq,                   // zone load to be met
     135             :                        int const ActuatedNode,                // node that controls unit output
     136             :                        Real64 const MaxFlow,                  // maximum water flow
     137             :                        Real64 const MinFlow,                  // minimum water flow
     138             :                        Real64 const ControlOffset,            // really the tolerance
     139             :                        int &ControlCompTypeNum,               // Internal type num for CompType
     140             :                        int &CompErrIndex,                     // for Recurring error call
     141             :                        Optional_int_const TempInNode,         // inlet node for output calculation
     142             :                        Optional_int_const TempOutNode,        // outlet node for output calculation
     143             :                        Optional<Real64 const> AirMassFlow,    // air mass flow rate
     144             :                        Optional_int_const Action,             // 1=reverse; 2=normal
     145             :                        Optional_int_const EquipIndex,         // Identifier for equipment of Outdoor Air Unit "ONLY"
     146             :                        PlantLocation const &plantLoc,         // for plant components, Location
     147             :                        Optional_int_const ControlledZoneIndex // controlled zone index for the zone containing the component
     148             : )
     149             : {
     150             : 
     151             :     // SUBROUTINE INFORMATION:
     152             :     //       AUTHOR         Richard J. Liesen
     153             :     //       DATE WRITTEN   April 2000
     154             :     //       MODIFIED       Brent Griffith, Sept 2010 update plant interactions
     155             :     //       RE-ENGINEERED  na
     156             : 
     157             :     // PURPOSE OF THIS SUBROUTINE:
     158             :     // The purpose of this subroutine is to control the output of heating or cooling
     159             :     // meet the zone load.
     160             : 
     161             :     // METHODOLOGY EMPLOYED:
     162             :     // Currently this is using an intervasl halving scheme to a control tolerance
     163             : 
     164             :     // Using/Aliasing
     165             :     using namespace DataLoopNode;
     166             :     using BaseboardRadiator::SimHWConvective;
     167             :     using FanCoilUnits::Calc4PipeFanCoil;
     168             : 
     169             :     using HWBaseboardRadiator::CalcHWBaseboard;
     170             :     using PlantUtilities::SetActuatedBranchFlowRate;
     171             :     using Psychrometrics::PsyCpAirFnW;
     172             :     using SteamBaseboardRadiator::CalcSteamBaseboard;
     173             :     using UnitHeater::CalcUnitHeaterComponents;
     174             :     using UnitVentilator::CalcUnitVentilatorComponents;
     175             :     using VentilatedSlab::CalcVentilatedSlabComps;
     176             :     using WaterCoils::SimulateWaterCoilComponents;
     177             : 
     178             :     // SUBROUTINE PARAMETER DEFINITIONS:
     179             :     // Iteration maximum for reheat control
     180             :     static int constexpr MaxIter(25);
     181             :     static Real64 const iter_fac(1.0 / std::pow(2, MaxIter - 3));
     182    11202809 :     int constexpr iReverseAction(1);
     183    11202809 :     int constexpr iNormalAction(2);
     184             : 
     185             :     // Note - order in routine must match order below
     186             :     //  Plus -- order in ListOfComponents array must be in sorted order.
     187    11202809 :     int constexpr NumComponents(11);
     188             :     static Array1D_string const ListOfComponents(NumComponents,
     189             :                                                  {"AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT",
     190             :                                                   "AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT",
     191             :                                                   "COIL:HEATING:WATER",
     192             :                                                   "ZONEHVAC:BASEBOARD:CONVECTIVE:WATER",
     193             :                                                   "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM",
     194             :                                                   "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER",
     195             :                                                   "ZONEHVAC:FOURPIPEFANCOIL",
     196             :                                                   "ZONEHVAC:OUTDOORAIRUNIT",
     197             :                                                   "ZONEHVAC:UNITHEATER",
     198             :                                                   "ZONEHVAC:UNITVENTILATOR",
     199    11202809 :                                                   "ZONEHVAC:VENTILATEDSLAB"});
     200             : 
     201             :     // DERIVED TYPE DEFINITIONS
     202             :     // Interval Half Type used for Controller
     203             : 
     204             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     205    11202809 :     int Iter(0);  // Iteration limit for the interval halving process
     206             :     Real64 CpAir; // specific heat of air (J/kg-C)
     207             :     bool Converged;
     208             :     Real64 Denom;   // the denominator of the control signal
     209             :     Real64 LoadMet; // Actual output of unit (watts)
     210             :     // INTEGER, SAVE    :: ErrCount=0  ! Number of times that the maximum iterations was exceeded
     211             :     // INTEGER, SAVE    :: ErrCount1=0 ! for recurring error
     212             :     bool WaterCoilAirFlowControl; // True if controlling air flow through water coil, water flow fixed
     213             :     int SimCompNum;               // internal number for case statement
     214    11202809 :     Real64 HalvingPrec(0.0);      // precision of halving algorithm
     215             :     bool BBConvergeCheckFlag;     // additional check on convergence specifically for radiant/convective baseboard units
     216             : 
     217             :     // Object Data
     218    11202809 :     auto &ZoneInterHalf = state.dataGeneralRoutines->ZoneInterHalf;
     219    11202809 :     auto &ZoneController = state.dataGeneralRoutines->ZoneController;
     220             : 
     221    11202809 :     if (ControlCompTypeNum != 0) {
     222    11200718 :         SimCompNum = ControlCompTypeNum;
     223             :     } else {
     224        2091 :         SimCompNum = UtilityRoutines::FindItemInSortedList(CompType, ListOfComponents, NumComponents);
     225        2091 :         ControlCompTypeNum = SimCompNum;
     226             :     }
     227             : 
     228    11202809 :     Iter = 0;
     229    11202809 :     Converged = false;
     230    11202809 :     WaterCoilAirFlowControl = false;
     231    11202809 :     LoadMet = 0.0;
     232    11202809 :     HalvingPrec = 0.0;
     233             : 
     234             :     // At the beginning of every time step the value is reset to the User Input
     235    11202809 :     ZoneController.SetPoint = 0.0;
     236             : 
     237             :     // Set to converged controller
     238    11202809 :     ZoneInterHalf.MaxFlowCalc = true;
     239    11202809 :     ZoneInterHalf.MinFlowCalc = false;
     240    11202809 :     ZoneInterHalf.NormFlowCalc = false;
     241    11202809 :     ZoneInterHalf.MinFlowResult = false;
     242    11202809 :     ZoneInterHalf.MaxResult = 1.0;
     243    11202809 :     ZoneInterHalf.MinResult = 0.0;
     244             : 
     245             :     // Start the Solution Iteration
     246   153371051 :     while (!Converged) {
     247             : 
     248    78132205 :         if (FirstHVACIteration) {
     249    32568867 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail = MaxFlow;
     250    32568867 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail = MinFlow;
     251             :             // Check to make sure that the Minimum Flow rate is less than the max.
     252    32568867 :             if (MinFlow > MaxFlow) {
     253           0 :                 ShowSevereError(state, "ControlCompOutput:" + CompType + ':' + CompName + ", Min Control Flow is > Max Control Flow");
     254           0 :                 ShowContinueError(
     255           0 :                     state, format("Acuated Node={} MinFlow=[{:.3T}], Max Flow={:.3T}", state.dataLoopNodes->NodeID(ActuatedNode), MinFlow, MaxFlow));
     256           0 :                 ShowContinueErrorTimeStamp(state, "");
     257           0 :                 ShowFatalError(state, "Program terminates due to preceding condition.");
     258             :             }
     259             :         } // End of FirstHVACIteration Conditional If
     260             :         // The interface managers can reset the Max or Min to available values during the time step
     261             :         // and these will then be the new setpoint limits for the controller to work within.
     262    78132205 :         if ((SimCompNum == 3) && (!present(AirMassFlow))) {
     263     6057749 :             ZoneController.MaxSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail;
     264     6057749 :             ZoneController.MinSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail;
     265             :         } else {
     266    72074456 :             ZoneController.MaxSetPoint =
     267    72074456 :                 min(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMax);
     268    72074456 :             ZoneController.MinSetPoint =
     269    72074456 :                 max(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMin);
     270             :         }
     271             :         // The first time through run at maximum flow rate and find results
     272    78132205 :         if (ZoneInterHalf.MaxFlowCalc) {
     273    11202809 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     274    11202809 :             ZoneInterHalf.MaxFlow = ZoneController.MaxSetPoint;
     275    11202809 :             ZoneInterHalf.MaxFlowCalc = false;
     276    11202809 :             ZoneInterHalf.MinFlowCalc = true;
     277             :             // Record the maximum flow rates and set the flow to the minimum and find results
     278    66929396 :         } else if (ZoneInterHalf.MinFlowCalc) {
     279    11104366 :             ZoneInterHalf.MaxResult = ZoneController.SensedValue;
     280    11104366 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     281    11104366 :             ZoneInterHalf.MinFlow = ZoneController.MinSetPoint;
     282    11104366 :             ZoneInterHalf.MinFlowCalc = false;
     283    11104366 :             ZoneInterHalf.MinFlowResult = true;
     284             :             // Record the minimum results and set flow to half way between the max and min and find results
     285    55825030 :         } else if (ZoneInterHalf.MinFlowResult) {
     286    11053427 :             ZoneInterHalf.MinResult = ZoneController.SensedValue;
     287    11053427 :             HalvingPrec = (ZoneInterHalf.MaxResult - ZoneInterHalf.MinResult) * iter_fac;
     288    11053427 :             ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     289    11053427 :             ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     290    11053427 :             ZoneInterHalf.MinFlowResult = false;
     291    11053427 :             ZoneInterHalf.NormFlowCalc = true;
     292             :             // Record the Mid results and check all possibilities and start interval halving procedure
     293    44771603 :         } else if (ZoneInterHalf.NormFlowCalc) {
     294    44771603 :             ZoneInterHalf.MidResult = ZoneController.SensedValue;
     295             : 
     296             :             // First check to see if the component is running; if not converge and return
     297    44771603 :             if (ZoneInterHalf.MaxResult == ZoneInterHalf.MinResult) {
     298             :                 // Set to converged controller
     299     1150330 :                 Converged = true;
     300     1150330 :                 ZoneInterHalf.MaxFlowCalc = true;
     301     1150330 :                 ZoneInterHalf.MinFlowCalc = false;
     302     1150330 :                 ZoneInterHalf.NormFlowCalc = false;
     303     1150330 :                 ZoneInterHalf.MinFlowResult = false;
     304     1150330 :                 ZoneInterHalf.MaxResult = 1.0;
     305     1150330 :                 ZoneInterHalf.MinResult = 0.0;
     306     1150330 :                 if ((SimCompNum >= 4) && (SimCompNum <= 6)) { // hot water baseboards use min flow
     307       19111 :                     ZoneController.CalculatedSetPoint = 0.0;  // CR7253
     308             :                 } else {
     309     1131219 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow; // CR7253
     310             :                 }
     311             :                 // Set the Actuated node MassFlowRate with zero value
     312     1150330 :                 if (plantLoc.loopNum) { // this is a plant component
     313      487411 :                     SetActuatedBranchFlowRate(state,
     314             :                                               ZoneController.CalculatedSetPoint,
     315             :                                               ActuatedNode,
     316             :                                               plantLoc,
     317             :                                               false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     318             :                 } else {                              // assume not a plant component
     319      662919 :                     state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     320             :                 }
     321     1150330 :                 return;
     322             :             }
     323             : 
     324             :             // The next series of checks is to determine what interval the current solution is in
     325             :             //   comparison to the setpoint and then respond appropriately.
     326             : 
     327             :             // Normal controller assumes that MaxResult will be greater than MinResult. First check
     328             :             // to make sure that this is the case
     329    43621273 :             if (ZoneInterHalf.MaxResult <= ZoneInterHalf.MinResult) {
     330      135336 :                 if (WaterCoilAirFlowControl) {
     331      135290 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     332             :                 } else {
     333          46 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     334             :                 }
     335             :                 // set to converged controller
     336      135336 :                 Converged = true;
     337      135336 :                 ZoneInterHalf.MaxFlowCalc = true;
     338      135336 :                 ZoneInterHalf.MinFlowCalc = false;
     339      135336 :                 ZoneInterHalf.NormFlowCalc = false;
     340      135336 :                 ZoneInterHalf.MinFlowResult = false;
     341      135336 :                 ZoneInterHalf.MaxResult = 1.0;
     342      135336 :                 ZoneInterHalf.MinResult = 0.0;
     343             :                 // MaxResult is greater than MinResult so simulation control algorithm may proceed normally
     344    43485937 :             } else if (ZoneInterHalf.MaxResult > ZoneInterHalf.MinResult) {
     345             :                 // Now check to see if the setpoint is outside the endpoints of the control range
     346             :                 // First check to see if the water is too cold and if so set to the minimum flow.
     347    43485937 :                 if (ZoneController.SetPoint <= ZoneInterHalf.MinResult) {
     348        7007 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     349             :                     // Set to Converged Controller
     350        7007 :                     Converged = true;
     351        7007 :                     ZoneInterHalf.MaxFlowCalc = true;
     352        7007 :                     ZoneInterHalf.MinFlowCalc = false;
     353        7007 :                     ZoneInterHalf.NormFlowCalc = false;
     354        7007 :                     ZoneInterHalf.MinFlowResult = false;
     355        7007 :                     ZoneInterHalf.MaxResult = 1.0;
     356        7007 :                     ZoneInterHalf.MinResult = 0.0;
     357             :                     // Then check if too hot and if so set it to the maximum flow
     358    43478930 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MaxResult) {
     359     4012365 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     360             :                     // Set to Converged Controller
     361     4012365 :                     Converged = true;
     362     4012365 :                     ZoneInterHalf.MaxFlowCalc = true;
     363     4012365 :                     ZoneInterHalf.MinFlowCalc = false;
     364     4012365 :                     ZoneInterHalf.NormFlowCalc = false;
     365     4012365 :                     ZoneInterHalf.MinFlowResult = false;
     366     4012365 :                     ZoneInterHalf.MaxResult = 1.0;
     367     4012365 :                     ZoneInterHalf.MinResult = 0.0;
     368             :                     // If between the max and mid set to new flow and raise min to mid
     369    39466565 :                 } else if ((ZoneController.SetPoint < ZoneInterHalf.MaxResult) && (ZoneController.SetPoint >= ZoneInterHalf.MidResult)) {
     370    17199952 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     371    17199952 :                     ZoneInterHalf.MinFlow = ZoneInterHalf.MidFlow;
     372    17199952 :                     ZoneInterHalf.MinResult = ZoneInterHalf.MidResult;
     373    17199952 :                     ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     374             :                     // If between the min and mid set to new flow and lower Max to mid
     375    22266613 :                 } else if ((ZoneController.SetPoint < ZoneInterHalf.MidResult) && (ZoneController.SetPoint > ZoneInterHalf.MinResult)) {
     376    22266613 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     377    22266613 :                     ZoneInterHalf.MaxFlow = ZoneInterHalf.MidFlow;
     378    22266613 :                     ZoneInterHalf.MaxResult = ZoneInterHalf.MidResult;
     379    22266613 :                     ZoneInterHalf.MidFlow = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     380             : 
     381             :                 } // End of the Conditional for the actual interval halving scheme itself
     382             :             }     // end of max > min check
     383             : 
     384             :         } // End of the Conditinal for the first 3 iterations for the interval halving
     385             : 
     386             :         // Make sure that the Calculated setpoint falls between the minimum and maximum allowed
     387    76981875 :         if (ZoneController.CalculatedSetPoint > ZoneController.MaxSetPoint) {
     388           0 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     389           0 :             Converged = true;
     390           0 :             ZoneInterHalf.MaxFlowCalc = true;
     391           0 :             ZoneInterHalf.MinFlowCalc = false;
     392           0 :             ZoneInterHalf.NormFlowCalc = false;
     393           0 :             ZoneInterHalf.MinFlowResult = false;
     394           0 :             ZoneInterHalf.MaxResult = 1.0;
     395           0 :             ZoneInterHalf.MinResult = 0.0;
     396    76981875 :         } else if (ZoneController.CalculatedSetPoint < ZoneController.MinSetPoint) {
     397          17 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     398          17 :             Converged = true;
     399          17 :             ZoneInterHalf.MaxFlowCalc = true;
     400          17 :             ZoneInterHalf.MinFlowCalc = false;
     401          17 :             ZoneInterHalf.NormFlowCalc = false;
     402          17 :             ZoneInterHalf.MinFlowResult = false;
     403          17 :             ZoneInterHalf.MaxResult = 1.0;
     404          17 :             ZoneInterHalf.MinResult = 0.0;
     405             :         }
     406             : 
     407             :         // check if hunting down around the limit of a significant mass flow in systems.
     408    76981875 :         if ((Iter > MaxIter / 2) && (ZoneController.CalculatedSetPoint < DataBranchAirLoopPlant::MassFlowTolerance)) {
     409           0 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     410           0 :             Converged = true;
     411           0 :             ZoneInterHalf.MaxFlowCalc = true;
     412           0 :             ZoneInterHalf.MinFlowCalc = false;
     413           0 :             ZoneInterHalf.NormFlowCalc = false;
     414           0 :             ZoneInterHalf.MinFlowResult = false;
     415           0 :             ZoneInterHalf.MaxResult = 1.0;
     416           0 :             ZoneInterHalf.MinResult = 0.0;
     417             :         }
     418             : 
     419             :         // Set the Actuated node MassFlowRate with the new value
     420    76981875 :         if (plantLoc.loopNum) { // this is a plant component
     421    71587045 :             SetActuatedBranchFlowRate(state,
     422             :                                       ZoneController.CalculatedSetPoint,
     423             :                                       ActuatedNode,
     424             :                                       plantLoc,
     425             :                                       false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     426             :         } else {                              // assume not a plant component, leave alone
     427     5394830 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     428             :         }
     429             : 
     430             :         // The denominator of the control signal should be no less than 100 watts
     431    76981875 :         Denom = sign(max(std::abs(QZnReq), 100.0), QZnReq);
     432    76981875 :         if (present(Action)) {
     433      764795 :             if (Action == iNormalAction) {
     434      540287 :                 Denom = max(std::abs(QZnReq), 100.0);
     435      224508 :             } else if (Action == iReverseAction) {
     436      224508 :                 Denom = -max(std::abs(QZnReq), 100.0);
     437             :             } else {
     438           0 :                 ShowFatalError(state, format("ControlCompOutput: Illegal Action argument =[{}]", Action));
     439             :             }
     440             :         }
     441             : 
     442    76981875 :         switch (SimCompNum) {      // Tuned If block changed to switch
     443      107839 :         case ParallelPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT'
     444             :             // simulate series piu reheat coil
     445      107839 :             SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     446             :             // Calculate the control signal (the variable we are forcing to zero)
     447      107839 :             CpAir =
     448      107839 :                 PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     449      215678 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     450      215678 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     451      107839 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     452      107839 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     453      107839 :             break;
     454             : 
     455      610245 :         case SeriesPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT'
     456             :             // simulate series piu reheat coil
     457      610245 :             SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     458             :             // Calculate the control signal (the variable we are forcing to zero)
     459      610245 :             CpAir =
     460      610245 :                 PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     461     1220490 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     462     1220490 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     463      610245 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     464      610245 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     465      610245 :             break;
     466             : 
     467    73629611 :         case HeatingCoilWaterNum: // 'COIL:HEATING:WATER'
     468             :             // Simulate reheat coil for the VAV system
     469    73629611 :             SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     470             :             // Calculate the control signal (the variable we are forcing to zero)
     471    73629611 :             CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat);
     472    73629611 :             if (present(AirMassFlow)) {
     473    68234781 :                 LoadMet = AirMassFlow * CpAir * state.dataLoopNodes->Node(TempOutNode).Temp;
     474    68234781 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     475             :             } else {
     476     5394830 :                 WaterCoilAirFlowControl = true;
     477    10789660 :                 LoadMet = state.dataLoopNodes->Node(TempOutNode).MassFlowRate * CpAir *
     478    10789660 :                           (state.dataLoopNodes->Node(TempOutNode).Temp -
     479     5394830 :                            state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     480     5394830 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     481             :             }
     482    73629611 :             break;
     483             : 
     484      729231 :         case BBWaterConvOnlyNum: // 'ZONEHVAC:BASEBOARD:CONVECTIVE:WATER'
     485             :             // Simulate baseboard
     486      729231 :             SimHWConvective(state, CompNum, LoadMet);
     487             :             // Calculate the control signal (the variable we are forcing to zero)
     488      729231 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     489      729231 :             break;
     490             : 
     491       27946 :         case BBSteamRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM'
     492             :             // Simulate baseboard
     493       27946 :             CalcSteamBaseboard(state, CompNum, LoadMet);
     494             :             // Calculate the control signal (the variable we are forcing to zero)
     495       27946 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     496       27946 :             break;
     497             : 
     498      162274 :         case BBWaterRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER'
     499             :             // Simulate baseboard
     500      162274 :             CalcHWBaseboard(state, CompNum, LoadMet);
     501             :             // Calculate the control signal (the variable we are forcing to zero)
     502      162274 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     503      162274 :             break;
     504             : 
     505           0 :         case FourPipeFanCoilNum: // 'ZONEHVAC:FOURPIPEFANCOIL'
     506             :             // Simulate fancoil unit
     507           0 :             Calc4PipeFanCoil(state, CompNum, ControlledZoneIndex, FirstHVACIteration, LoadMet);
     508             :             // Calculate the control signal (the variable we are forcing to zero)
     509           0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     510           0 :             break;
     511             : 
     512      764795 :         case OutdoorAirUnitNum: //'ZONEHVAC:OUTDOORAIRUNIT'
     513             :             // Simulate outdoor air unit components
     514      764795 :             OutdoorAirUnit::CalcOAUnitCoilComps(
     515      764795 :                 state, CompNum, FirstHVACIteration, EquipIndex, LoadMet); // Autodesk:OPTIONAL EquipIndex used without PRESENT check
     516             :             // Calculate the control signal (the variable we are forcing to zero)
     517      764795 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     518      764795 :             break;
     519             : 
     520      166647 :         case UnitHeaterNum: // 'ZONEHVAC:UNITHEATER'
     521             :             // Simulate unit heater components
     522      166647 :             CalcUnitHeaterComponents(state, CompNum, FirstHVACIteration, LoadMet);
     523             :             // Calculate the control signal (the variable we are forcing to zero)
     524      166647 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     525      166647 :             break;
     526             : 
     527      351716 :         case UnitVentilatorNum: // 'ZONEHVAC:UNITVENTILATOR'
     528             :             // Simulate unit ventilator components
     529      351716 :             CalcUnitVentilatorComponents(state, CompNum, FirstHVACIteration, LoadMet);
     530             :             // Calculate the control signal (the variable we are forcing to zero)
     531      351716 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     532      351716 :             break;
     533             : 
     534      431571 :         case VentilatedSlabNum: // 'ZONEHVAC:VENTILATEDSLAB'
     535             :             // Simulate unit ventilator components
     536      431571 :             CalcVentilatedSlabComps(state, CompNum, FirstHVACIteration, LoadMet);
     537             :             // Calculate the control signal (the variable we are forcing to zero)
     538      431571 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     539      431571 :             break;
     540             : 
     541           0 :         default:
     542           0 :             ShowFatalError(state, format("ControlCompOutput: Illegal Component Number argument =[{}]", SimCompNum));
     543           0 :             break;
     544             :         }
     545             : 
     546             :         // Check for Controller convergence to see if within the offset
     547    76981875 :         if (std::abs(ZoneController.SensedValue) <= ControlOffset || std::abs(ZoneController.SensedValue) <= HalvingPrec) {
     548             :             // Set to converged controller
     549     5831977 :             Converged = true;
     550     5831977 :             ZoneInterHalf.MaxFlowCalc = true;
     551     5831977 :             ZoneInterHalf.MinFlowCalc = false;
     552     5831977 :             ZoneInterHalf.NormFlowCalc = false;
     553     5831977 :             ZoneInterHalf.MinFlowResult = false;
     554     5831977 :             ZoneInterHalf.MaxResult = 1.0;
     555     5831977 :             ZoneInterHalf.MinResult = 0.0;
     556     5831977 :             break;
     557             :         }
     558    71149898 :         if (!Converged) {
     559    66995173 :             BBConvergeCheckFlag = BBConvergeCheck(SimCompNum, ZoneInterHalf.MaxFlow, ZoneInterHalf.MinFlow);
     560    66995173 :             if (BBConvergeCheckFlag) {
     561             :                 // Set to converged controller
     562       65777 :                 Converged = true;
     563       65777 :                 ZoneInterHalf.MaxFlowCalc = true;
     564       65777 :                 ZoneInterHalf.MinFlowCalc = false;
     565       65777 :                 ZoneInterHalf.NormFlowCalc = false;
     566       65777 :                 ZoneInterHalf.MinFlowResult = false;
     567       65777 :                 ZoneInterHalf.MaxResult = 1.0;
     568       65777 :                 ZoneInterHalf.MinResult = 0.0;
     569       65777 :                 break;
     570             :             }
     571             :         }
     572             : 
     573    71084121 :         ++Iter;
     574    71084121 :         if ((Iter > MaxIter) && (!state.dataGlobal->WarmupFlag)) {
     575             :             // if ( CompErrIndex == 0 ) {
     576           0 :             ShowWarningMessage(state, "ControlCompOutput: Maximum iterations exceeded for " + CompType + " = " + CompName);
     577           0 :             ShowContinueError(state, format("... Load met       = {:.5T} W.", LoadMet));
     578           0 :             ShowContinueError(state, format("... Load requested = {:.5T} W.", QZnReq));
     579           0 :             ShowContinueError(state, format("... Error          = {:.8T} %.", std::abs((LoadMet - QZnReq) * 100.0 / Denom)));
     580           0 :             ShowContinueError(state, format("... Tolerance      = {:.8T} %.", ControlOffset * 100.0));
     581           0 :             ShowContinueError(state, "... Error          = (Load met - Load requested) / MAXIMUM(Load requested, 100)");
     582           0 :             ShowContinueError(state, format("... Actuated Node Mass Flow Rate ={:.9R} kg/s", state.dataLoopNodes->Node(ActuatedNode).MassFlowRate));
     583           0 :             ShowContinueErrorTimeStamp(state, "");
     584           0 :             ShowRecurringWarningErrorAtEnd(state,
     585           0 :                                            "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
     586             :                                            CompErrIndex,
     587           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     588           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     589             :                                            _,
     590             :                                            "%",
     591             :                                            "%");
     592             :             //}
     593           0 :             ShowRecurringWarningErrorAtEnd(state,
     594           0 :                                            "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
     595             :                                            CompErrIndex,
     596           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     597           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     598             :                                            _,
     599             :                                            "%",
     600             :                                            "%");
     601           0 :             break; // It will not converge this time
     602    71084121 :         } else if (Iter > MaxIter * 2) {
     603           0 :             break;
     604             :         }
     605             : 
     606             :     } // End of the Convergence Iteration
     607             : }
     608             : 
     609    66995173 : bool BBConvergeCheck(int const SimCompNum, Real64 const MaxFlow, Real64 const MinFlow)
     610             : {
     611             : 
     612             :     // FUNCTION INFORMATION:
     613             :     //       AUTHOR         Rick Strand
     614             :     //       DATE WRITTEN   November 2017
     615             : 
     616             :     // PURPOSE OF THIS SUBROUTINE:
     617             :     // This is an additional check for the radiant/convective baseboard units
     618             :     // to see if they are converged or the flow is sufficiently converged to
     619             :     // procede with the simulation.  With the radiant component to these systems,
     620             :     // the impact on the load met is more difficult to calculate and the impact
     621             :     // on the actual system output is not as well behaved as for convective
     622             :     // systems.  This additional check avoids excessive iterations and max
     623             :     // iteration warnings and provides sufficiently converged results.  It is
     624             :     // only called from ControlCompOutput.
     625             : 
     626             :     // Return Value
     627             :     bool BBConvergeCheck;
     628             : 
     629             :     // SUBROUTINE PARAMETER DEFINITIONS:
     630             :     static Real64 constexpr BBIterLimit(0.00001);
     631             : 
     632    66995173 :     if (SimCompNum != BBSteamRadConvNum && SimCompNum != BBWaterRadConvNum) {
     633             :         // For all zone equipment except radiant/convective baseboard (steam and water) units:
     634    66813598 :         BBConvergeCheck = false;
     635             :     } else {
     636             :         // For steam and water radiant/convective baseboard units:
     637      181575 :         if ((MaxFlow - MinFlow) > BBIterLimit) {
     638      115798 :             BBConvergeCheck = false;
     639             :         } else {
     640       65777 :             BBConvergeCheck = true;
     641             :         }
     642             :     }
     643             : 
     644    66995173 :     return BBConvergeCheck;
     645             : }
     646             : 
     647        6716 : void CheckSysSizing(EnergyPlusData &state,
     648             :                     std::string const &CompType, // Component Type (e.g. Chiller:Electric)
     649             :                     std::string const &CompName  // Component Name (e.g. Big Chiller)
     650             : )
     651             : {
     652             : 
     653             :     // SUBROUTINE INFORMATION:
     654             :     //       AUTHOR         Fred Buhl
     655             :     //       DATE WRITTEN   October 2002
     656             :     //       MODIFIED       na
     657             :     //       RE-ENGINEERED  na
     658             : 
     659             :     // PURPOSE OF THIS SUBROUTINE:
     660             :     // This routine is called when an "autosize" input is encountered in a component
     661             :     // sizing routine to check that the system sizing calculations have been done.
     662             : 
     663             :     // METHODOLOGY EMPLOYED:
     664             :     // Checks SysSizingRunDone flag. If false throws a fatal error.
     665             : 
     666        6716 :     if (!state.dataSize->SysSizingRunDone) {
     667           0 :         ShowSevereError(state, "For autosizing of " + CompType + ' ' + CompName + ", a system sizing run must be done.");
     668           0 :         if (state.dataSize->NumSysSizInput == 0) {
     669           0 :             ShowContinueError(state, "No \"Sizing:System\" objects were entered.");
     670             :         }
     671           0 :         if (!state.dataGlobal->DoSystemSizing) {
     672           0 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do System Sizing Calculation" set to Yes.)");
     673             :         }
     674           0 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     675             :     }
     676        6716 : }
     677             : 
     678        4815 : void CheckThisAirSystemForSizing(EnergyPlusData &state, int const AirLoopNum, bool &AirLoopWasSized)
     679             : {
     680             : 
     681             :     // SUBROUTINE INFORMATION:
     682             :     //       AUTHOR         B. Griffith
     683             :     //       DATE WRITTEN   October 2013
     684             :     //       MODIFIED       na
     685             :     //       RE-ENGINEERED  na
     686             : 
     687             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     688             :     int ThisAirSysSizineInputLoop;
     689             : 
     690        4815 :     AirLoopWasSized = false;
     691        4815 :     if (state.dataSize->SysSizingRunDone) {
     692       24990 :         for (ThisAirSysSizineInputLoop = 1; ThisAirSysSizineInputLoop <= state.dataSize->NumSysSizInput; ++ThisAirSysSizineInputLoop) {
     693       24982 :             if (state.dataSize->SysSizInput(ThisAirSysSizineInputLoop).AirLoopNum == AirLoopNum) {
     694        4465 :                 AirLoopWasSized = true;
     695        4465 :                 break;
     696             :             }
     697             :         }
     698             :     }
     699        4815 : }
     700             : 
     701       12785 : void CheckZoneSizing(EnergyPlusData &state,
     702             :                      std::string const &CompType, // Component Type (e.g. Chiller:Electric)
     703             :                      std::string const &CompName  // Component Name (e.g. Big Chiller)
     704             : )
     705             : {
     706             : 
     707             :     // SUBROUTINE INFORMATION:
     708             :     //       AUTHOR         Fred Buhl
     709             :     //       DATE WRITTEN   October 2002
     710             :     //       MODIFIED       na
     711             :     //       RE-ENGINEERED  na
     712             : 
     713             :     // PURPOSE OF THIS SUBROUTINE:
     714             :     // This routine is called when an "autosize" input is encountered in a component
     715             :     // sizing routine to check that the zone sizing calculations have been done.
     716             : 
     717             :     // METHODOLOGY EMPLOYED:
     718             :     // Checks ZoneSizingRunDone flag. If false throws a fatal error.
     719             : 
     720       12785 :     if (!state.dataSize->ZoneSizingRunDone) {
     721           0 :         ShowSevereError(state, "For autosizing of " + CompType + ' ' + CompName + ", a zone sizing run must be done.");
     722           0 :         if (state.dataSize->NumZoneSizingInput == 0) {
     723           0 :             ShowContinueError(state, "No \"Sizing:Zone\" objects were entered.");
     724             :         }
     725           0 :         if (!state.dataGlobal->DoZoneSizing) {
     726           0 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do Zone Sizing Calculation" set to Yes.)");
     727             :         }
     728           0 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     729             :     }
     730       12785 : }
     731             : 
     732         603 : void CheckThisZoneForSizing(EnergyPlusData &state,
     733             :                             int const ZoneNum, // zone index to be checked
     734             :                             bool &ZoneWasSized)
     735             : {
     736             : 
     737             :     // SUBROUTINE INFORMATION:
     738             :     //       AUTHOR         B. Griffith
     739             :     //       DATE WRITTEN   Oct 2013
     740             :     //       MODIFIED       na
     741             :     //       RE-ENGINEERED  na
     742             : 
     743             :     // PURPOSE OF THIS SUBROUTINE:
     744             :     // utility routine to see if a particular zone has a Sizing:Zone object for it
     745             :     // and that sizing was done.
     746             : 
     747             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     748             :     int ThisSizingInput;
     749             : 
     750         603 :     ZoneWasSized = false;
     751         603 :     if (state.dataSize->ZoneSizingRunDone) {
     752        8305 :         for (ThisSizingInput = 1; ThisSizingInput <= state.dataSize->NumZoneSizingInput; ++ThisSizingInput) {
     753        8305 :             if (state.dataSize->ZoneSizingInput(ThisSizingInput).ZoneNum == ZoneNum) {
     754         597 :                 ZoneWasSized = true;
     755         597 :                 break;
     756             :             }
     757             :         }
     758             :     }
     759         603 : }
     760             : 
     761       38147 : void ValidateComponent(EnergyPlusData &state,
     762             :                        std::string_view CompType,    // Component Type (e.g. Chiller:Electric)
     763             :                        std::string const &CompName,  // Component Name (e.g. Big Chiller)
     764             :                        bool &IsNotOK,                // .TRUE. if this component pair is invalid
     765             :                        std::string const &CallString // Context of this pair -- for error message
     766             : )
     767             : {
     768             : 
     769             :     // SUBROUTINE INFORMATION:
     770             :     //       AUTHOR         Linda Lawrie
     771             :     //       DATE WRITTEN   October 2002
     772             :     //       MODIFIED       na
     773             :     //       RE-ENGINEERED  na
     774             : 
     775             :     // PURPOSE OF THIS SUBROUTINE:
     776             :     // This subroutine can be called to validate the component type-name pairs that
     777             :     // are so much a part of the EnergyPlus input.  The main drawback to this validation
     778             :     // has been that the "GetInput" routine may not have been called and/or exists in
     779             :     // another module from the one with the list.  This means that validation must be
     780             :     // done later, perhaps after simulation has already started or perhaps raises an
     781             :     // array bound error instead.
     782             : 
     783             :     // METHODOLOGY EMPLOYED:
     784             :     // Uses existing routines in InputProcessor.  GetObjectItemNum uses the "standard"
     785             :     // convention of the Name of the item/object being the first Alpha Argument.
     786             : 
     787             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     788             :     int ItemNum;
     789             : 
     790       38147 :     IsNotOK = false;
     791             : 
     792       38147 :     ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, std::string{CompType}, CompName);
     793             : 
     794       38147 :     if (ItemNum < 0) {
     795           0 :         ShowSevereError(state, format("During {} Input, Invalid Component Type input={}", CallString, CompType));
     796           0 :         ShowContinueError(state, "Component name=" + CompName);
     797           0 :         IsNotOK = true;
     798       38147 :     } else if (ItemNum == 0) {
     799           0 :         ShowSevereError(state, "During " + CallString + " Input, Invalid Component Name input=" + CompName);
     800           0 :         ShowContinueError(state, format("Component type={}", CompType));
     801           0 :         IsNotOK = true;
     802             :     }
     803       38147 : }
     804             : 
     805          15 : void ValidateComponent(EnergyPlusData &state,
     806             :                        std::string const &CompType,    // Component Type (e.g. Chiller:Electric)
     807             :                        std::string const &CompValType, // Component "name" field type
     808             :                        std::string const &CompName,    // Component Name (e.g. Big Chiller)
     809             :                        bool &IsNotOK,                  // .TRUE. if this component pair is invalid
     810             :                        std::string const &CallString   // Context of this pair -- for error message
     811             : )
     812             : {
     813             : 
     814             :     // SUBROUTINE INFORMATION:
     815             :     //       AUTHOR         Linda Lawrie
     816             :     //       DATE WRITTEN   October 2002
     817             :     //       MODIFIED       na
     818             :     //       RE-ENGINEERED  na
     819             : 
     820             :     // PURPOSE OF THIS SUBROUTINE:
     821             :     // This subroutine can be called to validate the component type-name pairs that
     822             :     // are so much a part of the EnergyPlus input.  The main drawback to this validation
     823             :     // has been that the "GetInput" routine may not have been called and/or exists in
     824             :     // another module from the one with the list.  This means that validation must be
     825             :     // done later, perhaps after simulation has already started or perhaps raises an
     826             :     // array bound error instead.
     827             : 
     828             :     // METHODOLOGY EMPLOYED:
     829             :     // Uses existing routines in InputProcessor.  GetObjectItemNum uses the "standard"
     830             :     // convention of the Name of the item/object being the first Alpha Argument.
     831             : 
     832             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     833             :     int ItemNum;
     834             : 
     835          15 :     IsNotOK = false;
     836             : 
     837          15 :     ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, CompType, CompValType, CompName);
     838             : 
     839          15 :     if (ItemNum < 0) {
     840           0 :         ShowSevereError(state, "During " + CallString + " Input, Invalid Component Type input=" + CompType);
     841           0 :         ShowContinueError(state, "Component name=" + CompName);
     842           0 :         IsNotOK = true;
     843          15 :     } else if (ItemNum == 0) {
     844           0 :         ShowSevereError(state, "During " + CallString + " Input, Invalid Component Name input=" + CompName);
     845           0 :         ShowContinueError(state, "Component type=" + CompType);
     846           0 :         IsNotOK = true;
     847             :     }
     848          15 : }
     849             : 
     850       55347 : void CalcPassiveExteriorBaffleGap(EnergyPlusData &state,
     851             :                                   const Array1D_int &SurfPtrARR, // Array of indexes pointing to Surface structure in DataSurfaces
     852             :                                   Real64 const VentArea,         // Area available for venting the gap [m2]
     853             :                                   Real64 const Cv,               // Orifice coefficient for volume-based discharge, wind-driven [--]
     854             :                                   Real64 const Cd,               // Orifice coefficient for discharge,  buoyancy-driven [--]
     855             :                                   Real64 const HdeltaNPL,        // Height difference from neutral pressure level [m]
     856             :                                   Real64 const SolAbs,           // solar absorptivity of baffle [--]
     857             :                                   Real64 const AbsExt,           // thermal absorptance/emittance of baffle material [--]
     858             :                                   Real64 const Tilt,             // Tilt of gap [Degrees]
     859             :                                   Real64 const AspRat,           // aspect ratio of gap  Height/gap [--]
     860             :                                   Real64 const GapThick,         // Thickness of air space between baffle and underlying heat transfer surface
     861             :                                   DataSurfaces::SurfaceRoughness const Roughness, // Roughness index (1-6), see DataHeatBalance parameters
     862             :                                   Real64 const QdotSource,                        // Source/sink term, e.g. electricity exported from solar cell [W]
     863             :                                   Real64 &TsBaffle,                               // Temperature of baffle (both sides) use lagged value on input [C]
     864             :                                   Real64 &TaGap, // Temperature of air gap (assumed mixed) use lagged value on input [C]
     865             :                                   Optional<Real64> HcGapRpt,
     866             :                                   Optional<Real64> HrGapRpt,
     867             :                                   Optional<Real64> IscRpt,
     868             :                                   Optional<Real64> MdotVentRpt,
     869             :                                   Optional<Real64> VdotWindRpt,
     870             :                                   Optional<Real64> VdotBuoyRpt)
     871             : {
     872             : 
     873             :     // SUBROUTINE INFORMATION:
     874             :     //       AUTHOR         B.T. Griffith
     875             :     //       DATE WRITTEN   November 2004
     876             :     //       MODIFIED       BG March 2007 outdoor conditions from surface for height-dependent conditions
     877             :     //       RE-ENGINEERED  na
     878             : 
     879             :     // PURPOSE OF THIS SUBROUTINE:
     880             :     // model the effect of the a ventilated baffle covering the outside of a heat transfer surface.
     881             :     // return calculated temperatures and certain intermediate values for reporting
     882             : 
     883             :     // METHODOLOGY EMPLOYED:
     884             :     // Heat balances on baffle and air space.
     885             :     // Natural ventilation calculations use buoyancy and wind.
     886             : 
     887             :     // REFERENCES:
     888             :     // Nat. Vent. equations from ASHRAE HoF 2001 Chapt. 26
     889             : 
     890             :     // Using/Aliasing
     891             :     using ConvectionCoefficients::InitExteriorConvectionCoeff;
     892             :     using DataSurfaces::SurfaceData;
     893             :     using Psychrometrics::PsyCpAirFnW;
     894             :     using Psychrometrics::PsyRhoAirFnPbTdbW;
     895             :     using Psychrometrics::PsyWFnTdbTwbPb;
     896             : 
     897             :     // SUBROUTINE PARAMETER DEFINITIONS:
     898       55347 :     Real64 constexpr g(9.807);          // gravitational constant (m/s**2)
     899       55347 :     Real64 constexpr nu(15.66e-6);      // kinematic viscosity (m**2/s) for air at 300 K (Mills 1999 Heat Transfer)
     900       55347 :     Real64 constexpr k(0.0267);         // thermal conductivity (W/m K) for air at 300 K (Mills 1999 Heat Transfer)
     901       55347 :     Real64 constexpr Sigma(5.6697e-08); // Stefan-Boltzmann constant
     902             :     static constexpr std::string_view RoutineName("CalcPassiveExteriorBaffleGap");
     903             :     // INTERFACE BLOCK SPECIFICATIONS:
     904             : 
     905             :     // DERIVED TYPE DEFINITIONS:
     906             : 
     907             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     908             : 
     909             :     // following arrays are used to temporarily hold results from multiple underlying surfaces
     910      110694 :     Array1D<Real64> HSkyARR;
     911      110694 :     Array1D<Real64> HGroundARR;
     912      110694 :     Array1D<Real64> HAirARR;
     913      110694 :     Array1D<Real64> HPlenARR;
     914      110694 :     Array1D<Real64> HExtARR;
     915      110694 :     Array1D<Real64> LocalWindArr;
     916             : 
     917             :     // local working variables
     918             :     Real64 RhoAir;                // density of air
     919             :     Real64 CpAir;                 // specific heat of air
     920             :     Real64 Tamb;                  // outdoor drybulb
     921             :     Real64 A;                     // projected area of baffle from sum of underlying surfaces
     922             :     Real64 HcPlen;                // surface convection heat transfer coefficient for plenum surfaces
     923             :     int ThisSurf;                 // do loop counter
     924             :     int NumSurfs;                 // number of underlying HT surfaces associated with UTSC
     925             :     Real64 TmpTsBaf;              // baffle temperature
     926             :     int SurfPtr;                  // index of surface in main surface structure
     927             :     Real64 HMovInsul;             // dummy for call to InitExteriorConvectionCoeff
     928             :     Real64 HExt;                  // dummy for call to InitExteriorConvectionCoeff
     929             :     int ConstrNum;                // index of construction in main construction structure
     930             :     Real64 AbsThermSurf;          // thermal emmittance of underlying wall.
     931             :     Real64 TsoK;                  // underlying surface temperature in Kelvin
     932             :     Real64 TsBaffK;               // baffle temperature in Kelvin  (lagged)
     933             :     Real64 Vwind;                 // localized, and area-weighted average for wind speed
     934             :     Real64 HrSky;                 // radiation coeff for sky, area-weighted average
     935             :     Real64 HrGround;              // radiation coeff for ground, area-weighted average
     936             :     Real64 HrAtm;                 // radiation coeff for air (bulk atmosphere), area-weighted average
     937             :     Real64 Isc;                   // Incoming combined solar radiation, area-weighted average
     938             :     Real64 HrPlen;                // radiation coeff for plenum surfaces, area-weighted average
     939             :     Real64 Tso;                   // temperature of underlying surface, area-weighted average
     940             :     Real64 TmeanK;                // average of surface temps , for Beta in Grashoff no.
     941             :     Real64 Gr;                    // Grasshof number for natural convection calc
     942             :     Real64 VdotWind;              // volume flow rate of nat. vent due to wind
     943             :     Real64 VdotThermal;           // Volume flow rate of nat. vent due to buoyancy
     944             :     Real64 VdotVent;              // total volume flow rate of nat vent
     945             :     Real64 MdotVent;              // total mass flow rate of nat vent
     946             :     Real64 NuPlen;                // Nusselt No. for plenum Gap
     947             :     Real64 LocalOutDryBulbTemp;   // OutDryBulbTemp for here
     948             :     Real64 LocalWetBulbTemp;      // OutWetBulbTemp for here
     949             :     Real64 LocalOutHumRat;        // OutHumRat for here
     950       55347 :     bool ICSCollectorIsOn(false); // ICS collector has OSCM on
     951             :     int CollectorNum;             // current solar collector index
     952             :     Real64 ICSWaterTemp;          // ICS solar collector water temp
     953             :     Real64 ICSULossbottom;        // ICS solar collector bottom loss Conductance
     954       55347 :     Real64 sum_area = 0.0;
     955       55347 :     Real64 sum_produc_area_drybulb = 0.0;
     956       55347 :     Real64 sum_produc_area_wetbulb = 0.0;
     957      130893 :     for (int SurfNum : SurfPtrARR) {
     958       75546 :         sum_area += state.dataSurface->Surface(SurfNum).Area;
     959       75546 :         sum_produc_area_drybulb += state.dataSurface->Surface(SurfNum).Area * state.dataSurface->SurfOutDryBulbTemp(SurfNum);
     960       75546 :         sum_produc_area_wetbulb += state.dataSurface->Surface(SurfNum).Area * state.dataSurface->SurfOutWetBulbTemp(SurfNum);
     961             :     }
     962             :     //    LocalOutDryBulbTemp = sum( Surface( SurfPtrARR ).Area * Surface( SurfPtrARR ).OutDryBulbTemp ) / sum( Surface( SurfPtrARR ).Area );
     963       55347 :     LocalOutDryBulbTemp = sum_produc_area_drybulb / sum_area; // Autodesk:F2C++ Functions handle array subscript usage
     964             :     //    LocalWetBulbTemp = sum( Surface( SurfPtrARR ).Area * Surface( SurfPtrARR ).OutWetBulbTemp ) / sum( Surface( SurfPtrARR ).Area );
     965       55347 :     LocalWetBulbTemp = sum_produc_area_wetbulb / sum_area;
     966             : 
     967       55347 :     LocalOutHumRat = PsyWFnTdbTwbPb(state, LocalOutDryBulbTemp, LocalWetBulbTemp, state.dataEnvrn->OutBaroPress, RoutineName);
     968             : 
     969       55347 :     RhoAir = PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, LocalOutDryBulbTemp, LocalOutHumRat, RoutineName);
     970       55347 :     CpAir = PsyCpAirFnW(LocalOutHumRat);
     971       55347 :     if (!state.dataEnvrn->IsRain) {
     972       55347 :         Tamb = LocalOutDryBulbTemp;
     973             :     } else { // when raining we use wetbulb not drybulb
     974           0 :         Tamb = LocalWetBulbTemp;
     975             :     }
     976             :     //    A = sum( Surface( SurfPtrARR ).Area ); //Autodesk:F2C++ Array subscript usage: Replaced by below
     977       55347 :     A = sum_area;
     978       55347 :     TmpTsBaf = TsBaffle;
     979             : 
     980             :     // loop through underlying surfaces and collect needed data
     981       55347 :     NumSurfs = size(SurfPtrARR);
     982       55347 :     HSkyARR.dimension(NumSurfs, 0.0);
     983       55347 :     HGroundARR.dimension(NumSurfs, 0.0);
     984       55347 :     HAirARR.dimension(NumSurfs, 0.0);
     985       55347 :     LocalWindArr.dimension(NumSurfs, 0.0);
     986       55347 :     HPlenARR.dimension(NumSurfs, 0.0);
     987       55347 :     HExtARR.dimension(NumSurfs, 0.0);
     988             : 
     989      130893 :     for (ThisSurf = 1; ThisSurf <= NumSurfs; ++ThisSurf) {
     990       75546 :         SurfPtr = SurfPtrARR(ThisSurf);
     991             :         // Initializations for this surface
     992       75546 :         HMovInsul = 0.0;
     993       75546 :         LocalWindArr(ThisSurf) = state.dataSurface->SurfOutWindSpeed(SurfPtr);
     994       75546 :         InitExteriorConvectionCoeff(
     995       75546 :             state, SurfPtr, HMovInsul, Roughness, AbsExt, TmpTsBaf, HExtARR(ThisSurf), HSkyARR(ThisSurf), HGroundARR(ThisSurf), HAirARR(ThisSurf));
     996       75546 :         ConstrNum = state.dataSurface->Surface(SurfPtr).Construction;
     997       75546 :         AbsThermSurf = state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermal;
     998       75546 :         TsoK = state.dataHeatBalSurf->SurfOutsideTempHist(1)(SurfPtr) + DataGlobalConstants::KelvinConv;
     999       75546 :         TsBaffK = TmpTsBaf + DataGlobalConstants::KelvinConv;
    1000       75546 :         if (TsBaffK == TsoK) {        // avoid divide by zero
    1001           0 :             HPlenARR(ThisSurf) = 0.0; // no net heat transfer if same temperature
    1002             :         } else {
    1003       75546 :             HPlenARR(ThisSurf) = Sigma * AbsExt * AbsThermSurf * (pow_4(TsBaffK) - pow_4(TsoK)) / (TsBaffK - TsoK);
    1004             :         }
    1005             :         // Added for ICS collector OSCM
    1006       75546 :         if (state.dataSurface->SurfIsICS(SurfPtr)) {
    1007       12168 :             ICSCollectorIsOn = true;
    1008       12168 :             CollectorNum = state.dataSurface->SurfICSPtr(SurfPtr);
    1009             :         }
    1010             :     }
    1011             : 
    1012       55347 :     if (ICSCollectorIsOn) {
    1013       12168 :         if (state.dataGlobal->BeginEnvrnFlag && state.dataGeneralRoutines->MyICSEnvrnFlag) {
    1014          36 :             ICSULossbottom = 0.40;
    1015          36 :             ICSWaterTemp = 20.0;
    1016             :         } else {
    1017       12132 :             if (!state.dataSolarCollectors->Collector.allocated()) {
    1018           0 :                 ICSULossbottom = 0.40;
    1019           0 :                 ICSWaterTemp = 20.0;
    1020             :             } else {
    1021       12132 :                 ICSULossbottom = state.dataSolarCollectors->Collector(CollectorNum).UbLoss;
    1022       12132 :                 ICSWaterTemp = state.dataSolarCollectors->Collector(CollectorNum).TempOfWater;
    1023       12132 :                 state.dataGeneralRoutines->MyICSEnvrnFlag = false;
    1024             :             }
    1025             :         }
    1026             :     }
    1027       55347 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    1028       54913 :         state.dataGeneralRoutines->MyICSEnvrnFlag = true;
    1029             :     }
    1030             :     if (A == 0.0) { // should have been caught earlier
    1031             :     }
    1032       55347 :     auto Area(array_sub(state.dataSurface->Surface,
    1033             :                         &SurfaceData::Area,
    1034      110694 :                         SurfPtrARR)); // Autodesk:F2C++ Copy of subscripted Area array for use below: This makes a copy so review wrt performance
    1035             :     // now figure area-weighted averages from underlying surfaces.
    1036             :     //    Vwind = sum( LocalWindArr * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1037       55347 :     Vwind = sum(LocalWindArr * Area) / A;
    1038       55347 :     LocalWindArr.deallocate();
    1039             :     //    HrSky = sum( HSkyARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1040       55347 :     HrSky = sum(HSkyARR * Area) / A;
    1041       55347 :     HSkyARR.deallocate();
    1042             :     //    HrGround = sum( HGroundARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1043       55347 :     HrGround = sum(HGroundARR * Area) / A;
    1044       55347 :     HGroundARR.deallocate();
    1045             :     //    HrAtm = sum( HAirARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1046       55347 :     HrAtm = sum(HAirARR * Area) / A;
    1047       55347 :     HAirARR.deallocate();
    1048             :     //    HrPlen = sum( HPlenARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1049       55347 :     HrPlen = sum(HPlenARR * Area) / A;
    1050       55347 :     HPlenARR.deallocate();
    1051             :     //    HExt = sum( HExtARR * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1052       55347 :     HExt = sum(HExtARR * Area) / A;
    1053       55347 :     HExtARR.deallocate();
    1054             : 
    1055       55347 :     if (state.dataEnvrn->IsRain) HExt = 1000.0;
    1056             : 
    1057             :     //    Tso = sum( TH( 1, 1, SurfPtrARR ) * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1058       55347 :     Tso = sum_product_sub(state.dataHeatBalSurf->SurfOutsideTempHist(1), state.dataSurface->Surface, &SurfaceData::Area, SurfPtrARR) /
    1059             :           A; // Autodesk:F2C++ Functions handle array subscript usage
    1060             :     //    Isc = sum( SurfQRadSWOutIncident( SurfPtrARR ) * Surface( SurfPtrARR ).Area ) / A; //Autodesk:F2C++ Array subscript usage: Replaced by below
    1061       55347 :     Isc = sum_product_sub(state.dataHeatBal->SurfQRadSWOutIncident, state.dataSurface->Surface, &SurfaceData::Area, SurfPtrARR) /
    1062             :           A; // Autodesk:F2C++ Functions handle array subscript usage
    1063             : 
    1064       55347 :     TmeanK = 0.5 * (TmpTsBaf + Tso) + DataGlobalConstants::KelvinConv;
    1065             : 
    1066       55347 :     Gr = g * pow_3(GapThick) * std::abs(Tso - TmpTsBaf) * pow_2(RhoAir) / (TmeanK * pow_2(nu));
    1067             : 
    1068       55347 :     PassiveGapNusseltNumber(AspRat, Tilt, TmpTsBaf, Tso, Gr, NuPlen); // intentionally switch Tso to Tsi
    1069             : 
    1070       55347 :     HcPlen = NuPlen * (k / GapThick);
    1071             : 
    1072             :     // now model natural ventilation of plenum gap.
    1073       55347 :     VdotWind = Cv * (VentArea / 2.0) * Vwind;
    1074             : 
    1075       55347 :     if (TaGap > Tamb) {
    1076       43314 :         VdotThermal = Cd * (VentArea / 2.0) * std::sqrt(2.0 * g * HdeltaNPL * (TaGap - Tamb) / (TaGap + DataGlobalConstants::KelvinConv));
    1077       12033 :     } else if (TaGap == Tamb) {
    1078           0 :         VdotThermal = 0.0;
    1079             :     } else {
    1080       12033 :         if ((std::abs(Tilt) < 5.0) || (std::abs(Tilt - 180.0) < 5.0)) {
    1081        8793 :             VdotThermal = 0.0; // stable buoyancy situation
    1082             :         } else {
    1083        3240 :             VdotThermal = Cd * (VentArea / 2.0) * std::sqrt(2.0 * g * HdeltaNPL * (Tamb - TaGap) / (Tamb + DataGlobalConstants::KelvinConv));
    1084             :         }
    1085             :     }
    1086             : 
    1087       55347 :     VdotVent = VdotWind + VdotThermal;
    1088       55347 :     MdotVent = VdotVent * RhoAir;
    1089             : 
    1090             :     // now calculate baffle temperature
    1091       55347 :     if (!ICSCollectorIsOn) {
    1092       86358 :         TsBaffle = (Isc * SolAbs + HExt * Tamb + HrAtm * Tamb + HrSky * state.dataEnvrn->SkyTemp + HrGround * Tamb + HrPlen * Tso + HcPlen * TaGap +
    1093       43179 :                     QdotSource) /
    1094       43179 :                    (HExt + HrAtm + HrSky + HrGround + HrPlen + HcPlen);
    1095             :     } else {
    1096             : 
    1097       12168 :         TsBaffle = (ICSULossbottom * ICSWaterTemp + HrPlen * Tso + HcPlen * TaGap + QdotSource) / (ICSULossbottom + HrPlen + HcPlen);
    1098             :     }
    1099             :     // now calculate gap air temperature
    1100             : 
    1101       55347 :     TaGap = (HcPlen * A * Tso + MdotVent * CpAir * Tamb + HcPlen * A * TsBaffle) / (HcPlen * A + MdotVent * CpAir + HcPlen * A);
    1102             : 
    1103       55347 :     if (present(HcGapRpt)) HcGapRpt = HcPlen;
    1104       55347 :     if (present(HrGapRpt)) HrGapRpt = HrPlen;
    1105       55347 :     if (present(IscRpt)) IscRpt = Isc;
    1106       55347 :     if (present(MdotVentRpt)) MdotVentRpt = MdotVent;
    1107       55347 :     if (present(VdotWindRpt)) VdotWindRpt = VdotWind;
    1108       55347 :     if (present(VdotBuoyRpt)) VdotBuoyRpt = VdotThermal;
    1109       55347 : }
    1110             : 
    1111             : //****************************************************************************
    1112             : 
    1113       55347 : void PassiveGapNusseltNumber(Real64 const AspRat, // Aspect Ratio of Gap height to gap width
    1114             :                              Real64 const Tilt,   // Tilt of gap, degrees
    1115             :                              Real64 const Tso,    // Temperature of gap surface closest to outside (K)
    1116             :                              Real64 const Tsi,    // Temperature of gap surface closest to zone (K)
    1117             :                              Real64 const Gr,     // Gap gas Grashof number
    1118             :                              Real64 &gNu          // Gap gas Nusselt number
    1119             : )
    1120             : {
    1121             : 
    1122             :     // SUBROUTINE INFORMATION:
    1123             :     //       AUTHOR         Adapted by B. Griffith from Fred Winkelmann's from NusseltNumber in WindowManager.cc
    1124             :     //       DATE WRITTEN   September 2001
    1125             :     //       MODIFIED       B. Griffith November 2004  (same models but slightly different for general use)
    1126             :     //       RE-ENGINEERED  na
    1127             : 
    1128             :     // PURPOSE OF THIS SUBROUTINE:
    1129             :     // Finds the Nusselt number for air-filled gaps between isothermal solid layers.
    1130             : 
    1131             :     // METHODOLOGY EMPLOYED:
    1132             :     // Based on methodology in Chapter 5 of the July 18, 2001 draft of ISO 15099,
    1133             :     // "Thermal Performance of Windows, Doors and Shading Devices--Detailed Calculations."
    1134             :     // The equation numbers below correspond to those in the standard.
    1135             : 
    1136             :     // REFERENCES:
    1137             :     // Window5 source code; ISO 15099
    1138             : 
    1139             :     // Using/Aliasing
    1140             :     // Locals
    1141             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    1142             : 
    1143             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1144       55347 :     Real64 constexpr Pr(0.71); // Prandtl number for air
    1145             : 
    1146             :     // INTERFACE BLOCK SPECIFICATIONS
    1147             : 
    1148             :     // DERIVED TYPE DEFINITIONS
    1149             : 
    1150             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS
    1151             :     Real64 Ra;     // Rayleigh number
    1152             :     Real64 gnu901; // Nusselt number temporary variables for
    1153             :     Real64 gnu902;
    1154             :     Real64 gnu90;
    1155             :     Real64 gnu601;
    1156             :     Real64 gnu602; // different tilt and Ra ranges
    1157             :     Real64 gnu60;
    1158             :     Real64 gnu601a;
    1159             :     Real64 gnua;
    1160             :     Real64 gnub;
    1161             :     Real64 cra; // Temporary variables
    1162             :     Real64 a;
    1163             :     Real64 b;
    1164             :     Real64 g;
    1165             :     Real64 ang;
    1166             :     Real64 tiltr;
    1167             : 
    1168       55347 :     tiltr = Tilt * DataGlobalConstants::DegToRadians;
    1169       55347 :     Ra = Gr * Pr;
    1170             : 
    1171             :     if (Ra > 2.0e6) {
    1172             : 
    1173             :         // write(*,*)' error, outside range of Rayleigh number'
    1174             :     }
    1175             : 
    1176       55347 :     if (Ra <= 1.0e4) {
    1177       12277 :         gnu901 = 1.0 + 1.7596678e-10 * std::pow(Ra, 2.2984755); // eq. 51
    1178             :     }
    1179       55347 :     if (Ra > 1.0e4 && Ra <= 5.0e4) gnu901 = 0.028154 * std::pow(Ra, 0.4134); // eq. 50
    1180       55347 :     if (Ra > 5.0e4) gnu901 = 0.0673838 * std::pow(Ra, 1.0 / 3.0);            // eq. 49
    1181             : 
    1182       55347 :     gnu902 = 0.242 * std::pow(Ra / AspRat, 0.272); // eq. 52
    1183       55347 :     gnu90 = max(gnu901, gnu902);
    1184             : 
    1185       55347 :     if (Tso > Tsi) {                                 // window heated from above
    1186       20821 :         gNu = 1.0 + (gnu90 - 1.0) * std::sin(tiltr); // eq. 53
    1187             :     } else {                                         // window heated from below
    1188       34526 :         if (Tilt >= 60.0) {
    1189       18484 :             g = 0.5 * std::pow(1.0 + std::pow(Ra / 3160.0, 20.6), -0.1);     // eq. 47
    1190       18484 :             gnu601a = 1.0 + pow_7(0.0936 * std::pow(Ra, 0.314) / (1.0 + g)); // eq. 45
    1191       18484 :             gnu601 = std::pow(gnu601a, 0.142857);
    1192             : 
    1193             :             // For any aspect ratio
    1194       18484 :             gnu602 = (0.104 + 0.175 / AspRat) * std::pow(Ra, 0.283); // eq. 46
    1195       18484 :             gnu60 = max(gnu601, gnu602);
    1196             : 
    1197             :             // linear interpolation for layers inclined at angles between 60 and 90 deg
    1198       18484 :             gNu = ((90.0 - Tilt) * gnu60 + (Tilt - 60.0) * gnu90) / 30.0;
    1199             :         }
    1200       34526 :         if (Tilt < 60.0) { // eq. 42
    1201       16042 :             cra = Ra * std::cos(tiltr);
    1202       16042 :             a = 1.0 - 1708.0 / cra;
    1203       16042 :             b = std::pow(cra / 5830.0, 0.33333) - 1.0;
    1204       16042 :             gnua = (std::abs(a) + a) / 2.0;
    1205       16042 :             gnub = (std::abs(b) + b) / 2.0;
    1206       16042 :             ang = 1708.0 * std::pow(std::sin(1.8 * tiltr), 1.6);
    1207       16042 :             gNu = 1.0 + 1.44 * gnua * (1.0 - ang / cra) + gnub;
    1208             :         }
    1209             :     }
    1210       55347 : }
    1211             : 
    1212     4297913 : void CalcBasinHeaterPower(EnergyPlusData &state,
    1213             :                           Real64 const Capacity,     // Basin heater capacity per degree C below setpoint (W/C)
    1214             :                           int const SchedulePtr,     // Pointer to basin heater schedule
    1215             :                           Real64 const SetPointTemp, // setpoint temperature for basin heater operation (C)
    1216             :                           Real64 &Power              // Basin heater power (W)
    1217             : )
    1218             : {
    1219             : 
    1220             :     // SUBROUTINE INFORMATION:
    1221             :     //       AUTHOR         Chandan Sharma, FSEC
    1222             :     //       DATE WRITTEN   Feb 2010
    1223             :     //       MODIFIED       na
    1224             :     //       RE-ENGINEERED  na
    1225             : 
    1226             :     // PURPOSE OF THIS SUBROUTINE:
    1227             :     // To calculate basin heater power when the evaporative cooled equipment is not operating
    1228             :     // and outdoor air dry-bulb temperature is below the set-point
    1229             : 
    1230             :     // METHODOLOGY EMPLOYED:
    1231             :     // Checks to see whether schedule for basin heater exists or not. If the schedule exists,
    1232             :     // the basin heater is operated for the schedule specified otherwise the heater runs
    1233             :     // for the entire simulation timestep whenever the outdoor temperature is below setpoint
    1234             :     // and water is not flowing through the evaporative cooled equipment.
    1235             : 
    1236             :     // REFERENCES:
    1237             :     // na
    1238             : 
    1239             :     // Using/Aliasing
    1240             :     using ScheduleManager::GetCurrentScheduleValue;
    1241             : 
    1242             :     // Locals
    1243             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    1244             : 
    1245             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1246             :     // na
    1247             : 
    1248             :     // INTERFACE BLOCK SPECIFICATIONS
    1249             :     // na
    1250             : 
    1251             :     // DERIVED TYPE DEFINITIONS
    1252             :     // na
    1253             : 
    1254             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1255             :     Real64 BasinHeaterSch; // Schedule for basin heater operation
    1256             : 
    1257     4297913 :     Power = 0.0;
    1258             :     // Operate basin heater anytime outdoor temperature is below setpoint and water is not flowing through the equipment
    1259             :     // IF schedule exists, basin heater performance can be scheduled OFF
    1260     4297913 :     if (SchedulePtr > 0) {
    1261       24445 :         BasinHeaterSch = GetCurrentScheduleValue(state, SchedulePtr);
    1262       24445 :         if (Capacity > 0.0 && BasinHeaterSch > 0.0) {
    1263       24439 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
    1264             :         }
    1265             :     } else {
    1266             :         // IF schedule does not exist, basin heater operates anytime outdoor dry-bulb temp is below setpoint
    1267     4273468 :         if (Capacity > 0.0) {
    1268      134696 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
    1269             :         }
    1270             :     }
    1271     4297913 : }
    1272             : 
    1273         769 : void TestAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
    1274             : {
    1275             : 
    1276             :     // SUBROUTINE INFORMATION:
    1277             :     //       AUTHOR         Linda Lawrie
    1278             :     //       DATE WRITTEN   March 2003
    1279             :     //       MODIFIED       na
    1280             :     //       RE-ENGINEERED  na
    1281             : 
    1282             :     // PURPOSE OF THIS SUBROUTINE:
    1283             :     // This subroutine tests supply, return and overall air path integrity.
    1284             : 
    1285             :     // METHODOLOGY EMPLOYED:
    1286             :     // na
    1287             : 
    1288             :     // REFERENCES:
    1289             :     // na
    1290             : 
    1291             :     // Using/Aliasing
    1292             :     using namespace DataLoopNode;
    1293         769 :     auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
    1294             : 
    1295             :     // Locals
    1296             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    1297             : 
    1298             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1299             :     // na
    1300             : 
    1301             :     // INTERFACE BLOCK SPECIFICATIONS
    1302             :     // COMPILER-GENERATED INTERFACE MODULE: Thu Sep 29 07:54:46 2011
    1303             : 
    1304             :     // DERIVED TYPE DEFINITIONS
    1305             :     // na
    1306             : 
    1307             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1308             :     int Loop;
    1309             :     int Loop1;
    1310             :     int Loop2;
    1311             :     int Loop3;
    1312             :     int Count;
    1313             :     int TestNode;
    1314             :     bool errFlag;
    1315        1538 :     Array2D_int ValRetAPaths;
    1316        1538 :     Array2D_int NumRAPNodes;
    1317        1538 :     Array2D_int ValSupAPaths;
    1318        1538 :     Array2D_int NumSAPNodes;
    1319             : 
    1320         769 :     NumSAPNodes.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
    1321         769 :     NumRAPNodes.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
    1322         769 :     ValRetAPaths.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
    1323         769 :     ValSupAPaths.allocate(state.dataLoopNodes->NumOfNodes, NumPrimaryAirSys);
    1324         769 :     NumSAPNodes = 0;
    1325         769 :     NumRAPNodes = 0;
    1326         769 :     ValRetAPaths = 0;
    1327         769 :     ValSupAPaths = 0;
    1328             : 
    1329         769 :     TestSupplyAirPathIntegrity(state, errFlag);
    1330         769 :     if (errFlag) ErrFound = true;
    1331         769 :     TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
    1332         769 :     if (errFlag) ErrFound = true;
    1333             : 
    1334             :     // Final tests, look for duplicate nodes
    1335        1941 :     for (Loop = 1; Loop <= NumPrimaryAirSys; ++Loop) {
    1336        1172 :         if (ValRetAPaths(1, Loop) != 0) continue;
    1337          13 :         if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) continue;
    1338          13 :         ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
    1339             :     }
    1340             : 
    1341        1941 :     for (Loop = 1; Loop <= NumPrimaryAirSys; ++Loop) {
    1342      356164 :         for (Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
    1343      354992 :             TestNode = ValRetAPaths(Loop1, Loop);
    1344      354992 :             Count = 0;
    1345    15282950 :             for (Loop2 = 1; Loop2 <= NumPrimaryAirSys; ++Loop2) {
    1346    48814463 :                 for (Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
    1347    48814463 :                     if (Loop2 == Loop && Loop1 == Loop3) continue; // Don't count test node
    1348    48808588 :                     if (ValRetAPaths(Loop3, Loop2) == 0) break;
    1349    33880630 :                     if (ValRetAPaths(Loop3, Loop2) == TestNode) ++Count;
    1350             :                 }
    1351             :             }
    1352      354992 :             if (Count > 0) {
    1353           0 :                 ShowSevereError(state, "Duplicate Node detected in Return Air Paths");
    1354           0 :                 ShowContinueError(state, "Test Node=" + state.dataLoopNodes->NodeID(TestNode));
    1355           0 :                 ShowContinueError(state, "In Air Path=" + state.dataAirLoop->AirToZoneNodeInfo(Loop).AirLoopName);
    1356           0 :                 ErrFound = true;
    1357             :             }
    1358             :         }
    1359             :     }
    1360             : 
    1361         769 :     NumSAPNodes.deallocate();
    1362         769 :     NumRAPNodes.deallocate();
    1363         769 :     ValRetAPaths.deallocate();
    1364         769 :     ValSupAPaths.deallocate();
    1365         769 : }
    1366             : 
    1367         769 : void TestSupplyAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
    1368             : {
    1369             : 
    1370             :     // SUBROUTINE INFORMATION:
    1371             :     //       AUTHOR         Linda Lawrie
    1372             :     //       DATE WRITTEN   March 2003
    1373             :     //       MODIFIED       na
    1374             :     //       RE-ENGINEERED  na
    1375             : 
    1376             :     // PURPOSE OF THIS SUBROUTINE:
    1377             :     // This subroutine tests supply air path integrity and displays the loop for each branch.
    1378             :     // Also, input and output nodes.
    1379             : 
    1380             :     // Using/Aliasing
    1381             :     using namespace DataLoopNode;
    1382         769 :     auto &GetZoneSplitterInput(SplitterComponent::GetSplitterInput);
    1383             :     using namespace DataZoneEquipment;
    1384         769 :     auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
    1385             : 
    1386             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1387             :     int Count;
    1388        1538 :     std::string AirPathNodeName;    // Air Path Inlet Node Name
    1389        1538 :     std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
    1390        1538 :     Array1D_bool FoundSupplyPlenum;
    1391        1538 :     Array1D_bool FoundZoneSplitter;
    1392        1538 :     Array1D_string FoundNames;
    1393         769 :     int NumErr(0); // Error Counter //Autodesk:Init Initialization added
    1394             :     int BCount;
    1395             :     int Found;
    1396             :     int Count1;
    1397             :     int Count2;
    1398             : 
    1399             :     // Do by Paths
    1400         769 :     ShowMessage(state, "Testing Individual Supply Air Path Integrity");
    1401         769 :     ErrFound = false;
    1402             : 
    1403         769 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1404             :     static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
    1405         769 :     print(state.files.bnd, "{}\n", Format_700);
    1406         769 :     print(state.files.bnd, " #Supply Air Paths,{}\n", state.dataZoneEquip->NumSupplyAirPaths);
    1407             :     static constexpr std::string_view Format_702("! <Supply Air Path>,<Supply Air Path Count>,<Supply Air Path Name>,<AirLoopHVAC Name>");
    1408         769 :     print(state.files.bnd, "{}\n", Format_702);
    1409             :     static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
    1410         769 :     print(state.files.bnd, "{}\n", Format_703);
    1411             :     static constexpr std::string_view Format_704(
    1412             :         "! <Supply Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
    1413         769 :     print(state.files.bnd, "{}\n", Format_704);
    1414             :     static constexpr std::string_view Format_707("! <#Outlet Nodes on Supply Air Path Component>,<Number of Nodes>");
    1415         769 :     print(state.files.bnd, "{}\n", Format_707);
    1416             :     static constexpr std::string_view Format_708(
    1417             :         "! <Supply Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
    1418             :         "Node Name>,<AirLoopHVAC Name>");
    1419         769 :     print(state.files.bnd, "{}\n", Format_708);
    1420             : 
    1421        1945 :     for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1422             : 
    1423             :         // Determine which air loop this supply air path is connected to
    1424        1176 :         Found = 0;
    1425        6469 :         for (Count1 = 1; Count1 <= NumPrimaryAirSys; ++Count1) {
    1426        6469 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1427        6469 :             Found = 0;
    1428       12954 :             for (Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
    1429       12970 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
    1430        6485 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2))
    1431        1176 :                     Found = Count2;
    1432             :             }
    1433        6469 :             if (Found != 0) break;
    1434             :         }
    1435        1176 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
    1436             : 
    1437        1176 :         print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
    1438        1176 :         print(state.files.bnd, "   #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
    1439             : 
    1440        1176 :         AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
    1441             : 
    1442        2359 :         for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1443             : 
    1444        3549 :             print(state.files.bnd,
    1445             :                   "   Supply Air Path Component,{},{},{},{}\n",
    1446             :                   Count,
    1447        1183 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1448        1183 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1449        1183 :                   PrimaryAirLoopName);
    1450             : 
    1451        2366 :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
    1452        3549 :                 AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
    1453             : 
    1454        1183 :             switch (CompType) {
    1455           8 :             case AirLoopHVACCompType::SupplyPlenum: {
    1456          20 :                 for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
    1457          24 :                     if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
    1458          24 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
    1459           4 :                         continue;
    1460           8 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode)) {
    1461           0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
    1462           0 :                         ShowContinueError(state,
    1463           0 :                                           format("For AirLoopHVAC:SupplyPlenum={}", state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName));
    1464           0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
    1465           0 :                         ShowContinueError(state,
    1466           0 :                                           format("Encountered node name (supply plenum)={}",
    1467           0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(1))));
    1468           0 :                         ErrFound = true;
    1469           0 :                         ++NumErr;
    1470             :                     }
    1471          16 :                     print(state.files.bnd,
    1472             :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1473          16 :                           state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
    1474          20 :                     for (Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
    1475          84 :                         print(state.files.bnd,
    1476             :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1477             :                               Count1,
    1478          12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1479          12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1480          24 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
    1481          24 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
    1482          12 :                               PrimaryAirLoopName);
    1483             :                     }
    1484             :                 }
    1485           8 :             } break;
    1486        1175 :             case AirLoopHVACCompType::ZoneSplitter: {
    1487       12952 :                 for (Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
    1488       23554 :                     if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
    1489       23554 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
    1490       10602 :                         continue;
    1491        1175 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)) {
    1492           0 :                         ShowSevereError(state, "Error in AirLoopHVAC:SupplyPath=" + state.dataZoneEquip->SupplyAirPath(BCount).Name);
    1493           0 :                         ShowContinueError(state, "For AirLoopHVAC:ZoneSplitter=" + state.dataSplitterComponent->SplitterCond(Count2).SplitterName);
    1494           0 :                         ShowContinueError(state, "Expected inlet node (supply air path)=" + AirPathNodeName);
    1495           0 :                         ShowContinueError(state,
    1496           0 :                                           "Encountered node name (zone splitter)=" +
    1497           0 :                                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode));
    1498           0 :                         ErrFound = true;
    1499           0 :                         ++NumErr;
    1500             :                     }
    1501        2350 :                     print(state.files.bnd,
    1502             :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1503        2350 :                           state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
    1504        4650 :                     for (Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
    1505       24325 :                         print(state.files.bnd,
    1506             :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1507             :                               Count1,
    1508        3475 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1509        3475 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1510        6950 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
    1511        6950 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
    1512        3475 :                               PrimaryAirLoopName);
    1513             :                     }
    1514             :                 }
    1515        1175 :             } break;
    1516           0 :             default: {
    1517           0 :                 ShowSevereError(state,
    1518           0 :                                 "Invalid Component Type in Supply Air Path=" + state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count));
    1519           0 :                 ErrFound = true;
    1520           0 :                 ++NumErr;
    1521           0 :             } break;
    1522             :             }
    1523             :         }
    1524             : 
    1525        1176 :         if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
    1526             :             static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
    1527        1176 :             print(state.files.bnd, "{}\n", Format_705);
    1528             :             static constexpr std::string_view Format_706("! <Supply Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1529        1176 :             print(state.files.bnd, "{}\n", Format_706);
    1530        1176 :             print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
    1531        5839 :             for (Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
    1532        4663 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
    1533        3528 :                     print(state.files.bnd,
    1534             :                           "   Supply Air Path Node,Inlet Node,{},{},{}\n",
    1535             :                           Count2,
    1536        2352 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1537        1176 :                           PrimaryAirLoopName);
    1538        3487 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
    1539          21 :                     print(state.files.bnd,
    1540             :                           "   Supply Air Path Node,Through Node,{},{},{}\n",
    1541             :                           Count2,
    1542          14 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1543           7 :                           PrimaryAirLoopName);
    1544        3480 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
    1545       10440 :                     print(state.files.bnd,
    1546             :                           "   Supply Air Path Node,Outlet Node,{},{},{}\n",
    1547             :                           Count2,
    1548        6960 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1549        3480 :                           PrimaryAirLoopName);
    1550             :                 }
    1551             :             }
    1552             :         }
    1553             :     }
    1554             : 
    1555         769 :     if (state.dataSplitterComponent->NumSplitters == 0) {
    1556         248 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
    1557           0 :             GetZoneSplitterInput(state);
    1558             :         }
    1559             :     }
    1560         769 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1561         583 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum") > 0) {
    1562           0 :             ZonePlenum::GetZonePlenumInput(state);
    1563             :         }
    1564             :     }
    1565             : 
    1566             :     // now the reverse.  is every zone splitter and supply plenum on supply air path
    1567         769 :     FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
    1568         769 :     FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
    1569         769 :     FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
    1570         777 :     for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1571          20 :         for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1572          35 :             for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1573          31 :                 if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1574           8 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM")
    1575          15 :                     continue;
    1576           8 :                 if (FoundSupplyPlenum(Count1)) {
    1577           0 :                     ShowSevereError(
    1578           0 :                         state, "AirLoopHVAC:SupplyPlenum=\"" + state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName + "\", duplicate entry.");
    1579           0 :                     ShowContinueError(state, "already exists on AirLoopHVAC:SupplyPath=\"" + FoundNames(Count1) + "\".");
    1580           0 :                     ErrFound = true;
    1581             :                 } else {
    1582             :                     // record use
    1583           8 :                     FoundSupplyPlenum(Count1) = true;
    1584           8 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1585             :                 }
    1586             :             }
    1587             :         }
    1588             :     }
    1589         769 :     FoundNames.deallocate();
    1590         769 :     FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
    1591        1944 :     for (Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1592       12952 :         for (BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1593       23565 :             for (Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1594       35364 :                 if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
    1595       36539 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1596        1175 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER")
    1597       10613 :                     continue;
    1598        1175 :                 if (FoundZoneSplitter(Count1)) {
    1599           0 :                     ShowSevereError(state,
    1600           0 :                                     "AirLoopHVAC:ZoneSplitter=\"" + state.dataSplitterComponent->SplitterCond(Count1).SplitterName +
    1601             :                                         "\", duplicate entry.");
    1602           0 :                     ShowContinueError(state, "already exists on AirLoopHVAC:SupplyPath=\"" + FoundNames(Count1) + "\".");
    1603           0 :                     ErrFound = true;
    1604             :                 } else {
    1605             :                     // record use
    1606        1175 :                     FoundZoneSplitter(Count1) = true;
    1607        1175 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1608             :                 }
    1609             :             }
    1610             :         }
    1611             :     }
    1612         769 :     FoundNames.deallocate();
    1613             : 
    1614         769 :     if (!all(FoundSupplyPlenum)) {
    1615           0 :         for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1616           0 :             if (FoundSupplyPlenum(Count1)) continue;
    1617           0 :             ShowSevereError(state,
    1618           0 :                             "AirLoopHVAC:SupplyPlenum=\"" + state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName +
    1619             :                                 "\", not found on any AirLoopHVAC:SupplyPath.");
    1620             :             //      ErrFound=.TRUE.
    1621             :         }
    1622             :     }
    1623             : 
    1624         769 :     if (!all(FoundZoneSplitter)) {
    1625           0 :         for (Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1626           0 :             if (FoundZoneSplitter(Count1)) continue;
    1627           0 :             ShowSevereError(state,
    1628           0 :                             "AirLoopHVAC:ZoneSplitter=\"" + state.dataSplitterComponent->SplitterCond(Count1).SplitterName +
    1629             :                                 "\", not found on any AirLoopHVAC:SupplyPath.");
    1630             :             //      ErrFound=.TRUE.
    1631             :         }
    1632             :     }
    1633             : 
    1634         769 :     FoundSupplyPlenum.deallocate();
    1635         769 :     FoundZoneSplitter.deallocate();
    1636             : 
    1637         769 :     if (ErrFound) {
    1638           0 :         ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
    1639             :     } else {
    1640         769 :         ShowMessage(state, "All Supply Air Paths passed integrity testing");
    1641             :     }
    1642         769 : }
    1643             : 
    1644         769 : void TestReturnAirPathIntegrity(EnergyPlusData &state, bool &ErrFound, Array2S_int ValRetAPaths)
    1645             : {
    1646             : 
    1647             :     // SUBROUTINE INFORMATION:
    1648             :     //       AUTHOR         Linda Lawrie
    1649             :     //       DATE WRITTEN   March 2003
    1650             : 
    1651             :     // PURPOSE OF THIS SUBROUTINE:
    1652             :     // This subroutine tests return air path integrity and displays the loop for each branch.
    1653             :     // Also, input and output nodes.
    1654             : 
    1655             :     // REFERENCES:
    1656             :     // Return Air Path Validity Rules:
    1657             :     //  Last component (zone mixer or zone return plenum) must resolve to
    1658             :     //  be the outlet node for the return air path.  Inlets to this component must be outlets from
    1659             :     //  previous components or "controlled zone outlets"?.
    1660             :     //  (though converse not true -- each outlet in previous components do not
    1661             :     //  have to be inlets on this item -- though they must be inputs somewhere in the stream).
    1662             :     //  If multiple components and no mixer, then a zone return plenums "outlet" must
    1663             :     //  be represented as an inlet on a later plenum.  i.e. some zone return plenums are
    1664             :     //  really acting as "mixers" in a sense.  These do not need to be stepwise in succession.
    1665             :     //  Same caveat for inlets from previous item.
    1666             :     //  If multiple components and mixer, then prior condition (nested plenums) is allowed as long as
    1667             :     //  those aren't duplicated as mixer inlets.  (i.e. zone rp 1 => zone rp 2 => zone mixer but
    1668             :     //  zone rp 1 outlet should not also be inlet to mixer.
    1669             :     //  Can have (nzrp -- nested zone return plenum, pzrp -- parallel zone return plenum):
    1670             :     //  nzrp 1 => nzrp 2 & pzrp 3 => zm (inlets from nzrp 2 and pzrp 3).  Or, likewise:
    1671             :     //  pzrp 1 & pzrp 2 => zm => pzrp 3 (outlets from pzrp 1/2 are inlets to zm whose outlet is an
    1672             :     //  inlet to pzrp 3 whose outlet is the outlet for the return air path.
    1673             : 
    1674             :     //  Cannot have duplicate nodes in the "inlet" stream?  (i.e. cannot have same zone feeding two independent
    1675             :     //  plenums, for example).  Similarly, Same return plenum can't be in two air loops nor as two independent
    1676             :     //  return plenums in one return air path.
    1677             : 
    1678             :     // Using/Aliasing
    1679             :     using namespace DataLoopNode;
    1680             :     using namespace DataZoneEquipment;
    1681             :     using namespace ZonePlenum;
    1682         769 :     auto &NumPrimaryAirSys = state.dataHVACGlobal->NumPrimaryAirSys;
    1683         769 :     auto &GetZoneMixerInput(MixerComponent::GetMixerInput);
    1684             :     using HVACSingleDuctInduc::FourPipeInductionUnitHasMixer;
    1685             :     using PoweredInductionUnits::PIUnitHasMixer;
    1686             :     using PurchasedAirManager::CheckPurchasedAirForReturnPlenum;
    1687             : 
    1688             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1689             :     int Loop;
    1690             :     int Count;
    1691        1538 :     std::string AirPathNodeName;    // Air Path Inlet Node Name
    1692        1538 :     std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
    1693        1538 :     Array1D_bool FoundReturnPlenum;
    1694        1538 :     Array1D_bool FoundZoneMixer;
    1695        1538 :     Array1D_string FoundNames;
    1696             :     int NumErr; // Error Counter
    1697             :     int BCount;
    1698             :     int Found;
    1699             :     int Count1;
    1700             :     int Count2;
    1701        1538 :     Array1D_int AllNodes;
    1702             :     int MixerCount;
    1703             :     int Count3;
    1704             :     int NumComp;
    1705             :     int CountNodes;
    1706             : 
    1707             :     // Formats
    1708             : 
    1709             :     // Do by Paths
    1710         769 :     ShowMessage(state, "Testing Individual Return Air Path Integrity");
    1711         769 :     ErrFound = false;
    1712         769 :     NumErr = 0;
    1713             : 
    1714         769 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1715             :     static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
    1716         769 :     print(state.files.bnd, "{}\n", Format_700);
    1717         769 :     print(state.files.bnd, " #Return Air Paths,{}\n", state.dataZoneEquip->NumReturnAirPaths);
    1718             :     static constexpr std::string_view Format_702("! <Return Air Path>,<Return Air Path Count>,<Return Air Path Name>,<AirLoopHVAC Name>");
    1719         769 :     print(state.files.bnd, "{}\n", Format_702);
    1720             :     static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
    1721         769 :     print(state.files.bnd, "{}\n", Format_703);
    1722             :     static constexpr std::string_view Format_704(
    1723             :         "! <Return Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
    1724         769 :     print(state.files.bnd, "{}\n", Format_704);
    1725             :     static constexpr std::string_view Format_707("! <#Inlet Nodes on Return Air Path Component>,<Number of Nodes>");
    1726         769 :     print(state.files.bnd, "{}\n", Format_707);
    1727             :     static constexpr std::string_view Format_708(
    1728             :         "! <Return Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
    1729             :         "Node Name>,<AirLoopHVAC Name>");
    1730         769 :     print(state.files.bnd, "{}\n", Format_708);
    1731             : 
    1732         769 :     AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
    1733             : 
    1734        1928 :     for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1735             :         //             Determine which air loop this supply air path is connected to
    1736        1159 :         Found = 0;
    1737        6438 :         for (Count1 = 1; Count1 <= NumPrimaryAirSys; ++Count1) {
    1738        6438 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1739        6438 :             Found = 0;
    1740       12876 :             for (Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
    1741       12876 :                 if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
    1742        6438 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2))
    1743        1159 :                     Found = Count2;
    1744             :             }
    1745        6438 :             if (Found != 0) break;
    1746             :         }
    1747        1159 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
    1748             : 
    1749        1159 :         print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
    1750             : 
    1751        1159 :         NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
    1752        1159 :         print(state.files.bnd, "   #Components on Return Air Path,{}\n", NumComp);
    1753             : 
    1754        1159 :         AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
    1755             : 
    1756        1159 :         MixerCount = 0;
    1757        2427 :         for (Count = 1; Count <= NumComp; ++Count) {
    1758        3804 :             print(state.files.bnd,
    1759             :                   "   Return Air Path Component,{},{},{},{}\n",
    1760             :                   Count,
    1761        1268 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
    1762        1268 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
    1763        1268 :                   PrimaryAirLoopName);
    1764             : 
    1765        1268 :             if (UtilityRoutines::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
    1766         997 :                 ++MixerCount;
    1767             :             }
    1768             :         }
    1769             : 
    1770        1159 :         if (MixerCount > 1) {
    1771           0 :             ShowSevereError(state, "Too many zone mixers in Return Air Path=" + state.dataZoneEquip->ReturnAirPath(BCount).Name);
    1772           0 :             ErrFound = true;
    1773           0 :             ++NumErr;
    1774           0 :             continue;
    1775             :         }
    1776             : 
    1777        1159 :         AllNodes = 0;
    1778        1159 :         CountNodes = 0;
    1779             : 
    1780        1159 :         if (NumComp > 0) {
    1781             : 
    1782        2318 :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
    1783        3477 :                 AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
    1784             : 
    1785        1159 :             switch (CompType) {
    1786         997 :             case AirLoopHVACCompType::ZoneMixer: {
    1787       12326 :                 for (Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1788       11329 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1789       10332 :                         continue;
    1790             :                     // Found correct Mixer (by name), check outlet node vs. return air path outlet node
    1791         997 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)) {
    1792           0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1793           0 :                         ShowContinueError(state, format("For Connector:Mixer={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1794           0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1795           0 :                         ShowContinueError(state,
    1796           0 :                                           format("Encountered node name (mixer)={}",
    1797           0 :                                                  state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)));
    1798           0 :                         ErrFound = true;
    1799           0 :                         ++NumErr;
    1800             :                     } else {
    1801         997 :                         ++CountNodes;
    1802         997 :                         AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
    1803        3282 :                         for (Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1804        2285 :                             ++CountNodes;
    1805        2285 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1806             :                         }
    1807             :                     }
    1808        1994 :                     print(state.files.bnd,
    1809             :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1810        1994 :                           state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
    1811        3282 :                     for (Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
    1812       15995 :                         print(state.files.bnd,
    1813             :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1814             :                               Count1,
    1815        2285 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1816        2285 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1817        4570 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
    1818        4570 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
    1819        2285 :                               PrimaryAirLoopName);
    1820             :                     }
    1821             :                 }
    1822         997 :             } break;
    1823         162 :             case AirLoopHVACCompType::ReturnPlenum: {
    1824         506 :                 for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1825         344 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
    1826         344 :                         state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1827         182 :                         continue;
    1828         162 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)) {
    1829           0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1830           0 :                         ShowContinueError(
    1831           0 :                             state, format("For AirLoopHVAC:ReturnPlenum={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1832           0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1833           0 :                         ShowContinueError(state,
    1834           0 :                                           format("Encountered node name (zone return plenum)={}",
    1835           0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)));
    1836           0 :                         ErrFound = true;
    1837           0 :                         ++NumErr;
    1838             :                     } else {
    1839         162 :                         ++CountNodes;
    1840         162 :                         AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
    1841         882 :                         for (Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1842         720 :                             ++CountNodes;
    1843         720 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1844             :                         }
    1845             :                     }
    1846         324 :                     print(state.files.bnd,
    1847             :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1848         324 :                           state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
    1849         882 :                     for (Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
    1850        5040 :                         print(state.files.bnd,
    1851             :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1852             :                               Count1,
    1853         720 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1854         720 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1855        1440 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
    1856        1440 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
    1857         720 :                               PrimaryAirLoopName);
    1858             :                     }
    1859             :                 }
    1860         162 :             } break;
    1861           0 :             default: // This already validated in GetReturnAirPath
    1862           0 :                 break;
    1863             :             }
    1864             :         }
    1865             : 
    1866        1159 :         if (NumComp > 1) {
    1867         217 :             for (Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
    1868             : 
    1869         218 :                 AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(getEnumerationValue(
    1870         327 :                     AirLoopHVACCompTypeNamesUC, UtilityRoutines::MakeUPPERCase(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
    1871             : 
    1872         109 :                 switch (CompType) {
    1873           0 :                 case AirLoopHVACCompType::ZoneMixer: {
    1874           0 :                     for (Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1875           0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1876           0 :                             continue;
    1877           0 :                         for (Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1878           0 :                             ++CountNodes;
    1879           0 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1880             :                         }
    1881             :                     }
    1882           0 :                 } break;
    1883         109 :                 case AirLoopHVACCompType::ReturnPlenum: {
    1884         430 :                     for (Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1885         321 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1886         321 :                             state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1887         212 :                             continue;
    1888         639 :                         for (Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1889         530 :                             ++CountNodes;
    1890         530 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1891             :                         }
    1892             :                     }
    1893         109 :                 } break;
    1894           0 :                 default: // This already validated in GetReturnAirPath
    1895           0 :                     break;
    1896             :                 }
    1897             :             }
    1898             :         }
    1899        1159 :         if (CountNodes > 0) {
    1900             :             static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
    1901        1159 :             print(state.files.bnd, "{}\n", Format_705);
    1902             :             static constexpr std::string_view Format_706("! <Return Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1903        1159 :             print(state.files.bnd, "{}\n", Format_706);
    1904        1159 :             print(state.files.bnd, "   #Nodes on Return Air Path,{}\n", CountNodes);
    1905        5853 :             for (Count2 = 1; Count2 <= CountNodes; ++Count2) {
    1906        4694 :                 if (Count2 == 1) {
    1907        3477 :                     print(state.files.bnd,
    1908             :                           "   Return Air Path Node,Outlet Node,{},{},{}\n",
    1909             :                           Count2,
    1910        2318 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1911        1159 :                           PrimaryAirLoopName);
    1912             :                 } else {
    1913       10605 :                     print(state.files.bnd,
    1914             :                           "   Return Air Path Node,Inlet Node,{},{},{}\n",
    1915             :                           Count2,
    1916        7070 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1917        3535 :                           PrimaryAirLoopName);
    1918             :                 }
    1919             :             }
    1920             :         }
    1921             :         // Determine Air Loop this Return Air Path is on
    1922        6438 :         for (Count2 = 1; Count2 <= NumPrimaryAirSys; ++Count2) {
    1923        6438 :             if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
    1924        6438 :                 if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
    1925        1159 :                     const auto WAirLoop = Count2;
    1926        1159 :                     ValRetAPaths(_, WAirLoop) = 0;
    1927        1159 :                     ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
    1928        1159 :                     break;
    1929             :                 }
    1930             :             } else {
    1931           0 :                 ShowWarningError(state,
    1932           0 :                                  "TestReturnAirPathIntegrity: Air Loop has no Zone Equipment Return Node=" +
    1933           0 :                                      state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName);
    1934             :             }
    1935             :         }
    1936             :     }
    1937             : 
    1938         769 :     AllNodes.deallocate();
    1939             : 
    1940         769 :     if (state.dataMixerComponent->NumMixers == 0) {
    1941         377 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
    1942           0 :             GetZoneMixerInput(state);
    1943             :         }
    1944             :     }
    1945         769 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1946         583 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum") > 0) {
    1947           0 :             GetZonePlenumInput(state);
    1948             :         }
    1949             :     }
    1950             : 
    1951             :     // now the reverse.  is every zone Mixer and Return plenum on Return air path
    1952         769 :     FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
    1953         769 :     FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
    1954         769 :     FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
    1955        1041 :     for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1956        1060 :         for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1957        1897 :             for (Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1958        1380 :                 if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1959         271 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM")
    1960         838 :                     continue;
    1961         271 :                 if (FoundReturnPlenum(Count1)) {
    1962           0 :                     ShowSevereError(
    1963           0 :                         state, "AirLoopHVAC:ReturnPlenum=\"" + state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName + "\", duplicate entry.");
    1964           0 :                     ShowContinueError(state, "already exists on AirLoopHVAC:ReturnPath=\"" + FoundNames(Count1) + "\".");
    1965           0 :                     ErrFound = true;
    1966             :                 } else {
    1967             :                     // record use
    1968         271 :                     FoundReturnPlenum(Count1) = true;
    1969         271 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1970             :                 }
    1971             :             }
    1972             :         }
    1973         272 :         if (CheckPurchasedAirForReturnPlenum(state, Count1)) FoundReturnPlenum(Count1) = true;
    1974             :     }
    1975         769 :     FoundNames.deallocate();
    1976         769 :     FoundNames.allocate(state.dataMixerComponent->NumMixers);
    1977        1806 :     for (Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1978       12405 :         for (BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1979       23175 :             for (Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1980       12804 :                 if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1981         997 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER")
    1982       10810 :                     continue;
    1983         997 :                 if (FoundZoneMixer(Count1)) {
    1984           0 :                     ShowSevereError(state,
    1985           0 :                                     "AirLoopHVAC:ZoneMixer=\"" + state.dataMixerComponent->MixerCond(Count1).MixerName + "\", duplicate entry.");
    1986           0 :                     ShowContinueError(state, "already exists on AirLoopHVAC:ReturnPath=\"" + FoundNames(Count1) + "\".");
    1987           0 :                     ErrFound = true;
    1988             :                 } else {
    1989             :                     // record use
    1990         997 :                     FoundZoneMixer(Count1) = true;
    1991         997 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1992             :                 }
    1993             :             }
    1994             :         }
    1995        1037 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1996             :             // PIU Units
    1997          40 :             if (PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
    1998             :         }
    1999        1037 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    2000             :             // fourPipeInduction units
    2001           6 :             if (FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
    2002             :         }
    2003        1037 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    2004             :             // Exhaust Systems
    2005           2 :             if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
    2006           2 :                 FoundZoneMixer(Count1) = true;
    2007             :         }
    2008             :     }
    2009         769 :     FoundNames.deallocate();
    2010             : 
    2011         769 :     if (!all(FoundReturnPlenum)) {
    2012           0 :         for (Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    2013           0 :             if (FoundReturnPlenum(Count1)) continue;
    2014           0 :             ShowSevereError(state,
    2015           0 :                             "AirLoopHVAC:ReturnPlenum=\"" + state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName +
    2016             :                                 "\", not found on any AirLoopHVAC:ReturnPath.");
    2017             :             //      ErrFound=.TRUE.
    2018             :         }
    2019             :     }
    2020             : 
    2021         769 :     if (!all(FoundZoneMixer)) {
    2022           0 :         for (Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    2023           0 :             if (FoundZoneMixer(Count1)) continue;
    2024           0 :             ShowSevereError(state,
    2025           0 :                             "AirLoopHVAC:ZoneMixer=\"" + state.dataMixerComponent->MixerCond(Count1).MixerName +
    2026             :                                 "\", not found on any AirLoopHVAC:ReturnPath, AirLoopHVAC:ExhaustSystem, AirTerminal:SingleDuct:SeriesPIU:Reheat,");
    2027           0 :             ShowContinueError(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat or AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction.");
    2028             :             //      ErrFound=.TRUE.
    2029             :         }
    2030             :     }
    2031             : 
    2032         769 :     FoundReturnPlenum.deallocate();
    2033         769 :     FoundZoneMixer.deallocate();
    2034             : 
    2035         769 :     if (ErrFound) {
    2036           0 :         ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
    2037             :     } else {
    2038         769 :         ShowMessage(state, "All Return Air Paths passed integrity testing");
    2039             :     }
    2040         769 : }
    2041             : 
    2042    33029470 : void CalcComponentSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    2043             :                                        Real64 const TDB2,      // dry-bulb temperature at state 2 {C}
    2044             :                                        Real64 const W2,        // humidity ratio at state 2
    2045             :                                        Real64 const TDB1,      // dry-bulb temperature at  at state 1 {C}
    2046             :                                        Real64 const W1,        // humidity ratio at state 1
    2047             :                                        Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    2048             :                                        Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    2049             :                                        Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    2050             : )
    2051             : {
    2052             : 
    2053             :     // Purpose:
    2054             :     // returns total, sensible and latent heat rate of change of moist air transitioning
    2055             :     // between two states. The moist air energy transfer can be cooling or heating process
    2056             :     // across a cooling, a heating coil, or an HVAC component.
    2057             : 
    2058             :     // Methodology:
    2059             :     // Q_total = m_dot * (h2 - h1)
    2060             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1);
    2061             :     // or Q_sensible = m_dot * cp_moistair_MinHumRat * (TDB2 - TDB1)
    2062             :     //    cp_moistair_MinHumRat = Psychrometrics::PsyCpAirFnW(min(W2, W1));
    2063             :     // Q_latent = Q_total - Q_latent;
    2064             : 
    2065    33029470 :     TotalOutput = 0.0;
    2066    33029470 :     LatentOutput = 0.0;
    2067    33029470 :     SensibleOutput = 0.0;
    2068    33029470 :     if (MassFlow > 0.0) {
    2069    32209248 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
    2070    32209248 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
    2071    32209248 :         LatentOutput = TotalOutput - SensibleOutput;                                                // latent addition/removal rate, {W}
    2072             :     }
    2073    33029470 : }
    2074             : 
    2075    51169907 : void CalcZoneSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    2076             :                                   Real64 const TDBEquip,  // dry-bulb temperature at equipment outlet {C}
    2077             :                                   Real64 const WEquip,    // humidity ratio at equipment outlet
    2078             :                                   Real64 const TDBZone,   // dry-bulb temperature at zone air node {C}
    2079             :                                   Real64 const WZone,     // humidity ratio at zone air node
    2080             :                                   Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    2081             :                                   Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    2082             :                                   Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    2083             : )
    2084             : {
    2085             : 
    2086             :     // Purpose:
    2087             :     // returns total, sensible and latent heat rate of transfer between the supply air zone inlet
    2088             :     // node and zone air node. The moist air energy transfer can be cooling or heating depending
    2089             :     // on the supply air zone inlet node and zone air node conditions.
    2090             : 
    2091             :     // Methodology:
    2092             :     // Q_total = m_dot * (hEquip - hZone)
    2093             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    2094             :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    2095             :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    2096             :     // Q_latent = Q_total - Q_latent;
    2097             : 
    2098    51169907 :     TotalOutput = 0.0;
    2099    51169907 :     LatentOutput = 0.0;
    2100    51169907 :     SensibleOutput = 0.0;
    2101    51169907 :     if (MassFlow > 0.0) {
    2102    79454620 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
    2103    39727310 :                                   Psychrometrics::PsyHFnTdbW(TDBZone, WZone));                         // total addition/removal rate, {W};
    2104    39727310 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    2105    39727310 :         LatentOutput = TotalOutput - SensibleOutput;                                                   // latent addition/removal rate, {W}
    2106             :     }
    2107    51169907 : }
    2108             : 
    2109    48112698 : Real64 calcZoneSensibleOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
    2110             :                               Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
    2111             :                               Real64 const TDBZone,  // dry-bulb temperature at zone air node {C}
    2112             :                               Real64 const WZone     // humidity ratio at zone air node
    2113             : )
    2114             : {
    2115             : 
    2116             :     // Purpose:
    2117             :     // returns sensible heat rate of transfer between the supply air zone inlet node and
    2118             :     // zone air node. The moist air energy transfer can be cooling or heating depending
    2119             :     // on the supply air zone inlet node and zone air node conditions.
    2120             : 
    2121             :     // Methodology:
    2122             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    2123             :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    2124             :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    2125             : 
    2126    48112698 :     Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
    2127    48112698 :     if (MassFlow > 0.0) {
    2128    39006409 :         sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    2129             :     }
    2130    48112698 :     return sensibleOutput;
    2131             : }
    2132        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13