LCOV - code coverage report
Current view: top level - EnergyPlus - GeneralRoutines.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 600 778 77.1 %
Date: 2024-08-24 18:31:18 Functions: 15 15 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, 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    11785742 : 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             :                        ObjexxFCL::Optional_int_const TempInNode,         // inlet node for output calculation
     142             :                        ObjexxFCL::Optional_int_const TempOutNode,        // outlet node for output calculation
     143             :                        ObjexxFCL::Optional<Real64 const> AirMassFlow,    // air mass flow rate
     144             :                        ObjexxFCL::Optional_int_const Action,             // 1=reverse; 2=normal
     145             :                        ObjexxFCL::Optional_int_const EquipIndex,         // Identifier for equipment of Outdoor Air Unit "ONLY"
     146             :                        PlantLocation const &plantLoc,                    // for plant components, Location
     147             :                        ObjexxFCL::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             : 
     156             :     // PURPOSE OF THIS SUBROUTINE:
     157             :     // The purpose of this subroutine is to control the output of heating or cooling
     158             :     // meet the zone load.
     159             : 
     160             :     // METHODOLOGY EMPLOYED:
     161             :     // Currently this is using an interval halving scheme to a control tolerance
     162             : 
     163             :     // SUBROUTINE PARAMETER DEFINITIONS:
     164             :     // Iteration maximum for reheat control
     165             :     static int constexpr MaxIter = 25;
     166             :     static Real64 const iter_fac = 1.0 / std::pow(2, MaxIter - 3);
     167    11785742 :     int constexpr iReverseAction = 1;
     168    11785742 :     int constexpr iNormalAction = 2;
     169             : 
     170             :     // Note - order in routine must match order below
     171             :     //  Plus -- order in ListOfComponents array must be in sorted order.
     172    11785742 :     int constexpr NumComponents = 11;
     173             :     static Array1D_string const ListOfComponents(NumComponents,
     174             :                                                  {"AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT",
     175             :                                                   "AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT",
     176             :                                                   "COIL:HEATING:WATER",
     177             :                                                   "ZONEHVAC:BASEBOARD:CONVECTIVE:WATER",
     178             :                                                   "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM",
     179             :                                                   "ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER",
     180             :                                                   "ZONEHVAC:FOURPIPEFANCOIL",
     181             :                                                   "ZONEHVAC:OUTDOORAIRUNIT",
     182             :                                                   "ZONEHVAC:UNITHEATER",
     183             :                                                   "ZONEHVAC:UNITVENTILATOR",
     184    11785742 :                                                   "ZONEHVAC:VENTILATEDSLAB"});
     185             : 
     186             :     // DERIVED TYPE DEFINITIONS
     187             :     // Interval Half Type used for Controller
     188             : 
     189             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     190             :     int SimCompNum; // internal number for case statement
     191             : 
     192             :     // Object Data
     193    11785742 :     auto &ZoneInterHalf = state.dataGeneralRoutines->ZoneInterHalf;
     194    11785742 :     auto &ZoneController = state.dataGeneralRoutines->ZoneController;
     195             : 
     196    11785742 :     if (ControlCompTypeNum != 0) {
     197    11783497 :         SimCompNum = ControlCompTypeNum;
     198             :     } else {
     199        2245 :         SimCompNum = Util::FindItemInSortedList(CompType, ListOfComponents, NumComponents);
     200        2245 :         ControlCompTypeNum = SimCompNum;
     201             :     }
     202             : 
     203    11785742 :     int Iter = 0; // Iteration limit for the interval halving process
     204    11785742 :     bool Converged = false;
     205    11785742 :     bool WaterCoilAirFlowControl = false; // True if controlling air flow through water coil, water flow fixed
     206    11785742 :     Real64 LoadMet = 0.0;                 // Actual output of unit (watts)
     207    11785742 :     Real64 HalvingPrec = 0.0;             // precision of halving algorithm
     208             :     Real64 CpAir;
     209             : 
     210             :     // At the beginning of every time step the value is reset to the User Input
     211    11785742 :     ZoneController.SetPoint = 0.0;
     212             : 
     213             :     // Set to converged controller
     214    11785742 :     ZoneInterHalf.MaxFlowCalc = true;
     215    11785742 :     ZoneInterHalf.MinFlowCalc = false;
     216    11785742 :     ZoneInterHalf.NormFlowCalc = false;
     217    11785742 :     ZoneInterHalf.MinFlowResult = false;
     218    11785742 :     ZoneInterHalf.MaxResult = 1.0;
     219    11785742 :     ZoneInterHalf.MinResult = 0.0;
     220             : 
     221             :     // Start the Solution Iteration
     222    87403761 :     while (!Converged) {
     223             : 
     224    83059555 :         if (FirstHVACIteration) {
     225    34793782 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail = MaxFlow;
     226    34793782 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail = MinFlow;
     227             :             // Check to make sure that the Minimum Flow rate is less than the max.
     228    34793782 :             if (MinFlow > MaxFlow) {
     229           0 :                 ShowSevereError(state, format("ControlCompOutput:{}:{}, Min Control Flow is > Max Control Flow", CompType, CompName));
     230           0 :                 ShowContinueError(
     231           0 :                     state, format("Acuated Node={} MinFlow=[{:.3T}], Max Flow={:.3T}", state.dataLoopNodes->NodeID(ActuatedNode), MinFlow, MaxFlow));
     232           0 :                 ShowContinueErrorTimeStamp(state, "");
     233           0 :                 ShowFatalError(state, "Program terminates due to preceding condition.");
     234             :             }
     235             :         } // End of FirstHVACIteration Conditional If
     236             :         // The interface managers can reset the Max or Min to available values during the time step
     237             :         // and these will then be the new setpoint limits for the controller to work within.
     238    83059555 :         if ((SimCompNum == 3) && (!present(AirMassFlow))) {
     239     6031080 :             ZoneController.MaxSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail;
     240     6031080 :             ZoneController.MinSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail;
     241             :         } else {
     242    77028475 :             ZoneController.MaxSetPoint =
     243    77028475 :                 min(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMax);
     244    77028475 :             ZoneController.MinSetPoint =
     245    77028475 :                 max(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMin);
     246             :         }
     247             :         // The first time through run at maximum flow rate and find results
     248    83059555 :         if (ZoneInterHalf.MaxFlowCalc) {
     249    11785742 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     250    11785742 :             ZoneInterHalf.MaxFlow = ZoneController.MaxSetPoint;
     251    11785742 :             ZoneInterHalf.MaxFlowCalc = false;
     252    11785742 :             ZoneInterHalf.MinFlowCalc = true;
     253             :             // Record the maximum flow rates and set the flow to the minimum and find results
     254    71273813 :         } else if (ZoneInterHalf.MinFlowCalc) {
     255    11686943 :             ZoneInterHalf.MaxResult = ZoneController.SensedValue;
     256    11686943 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     257    11686943 :             ZoneInterHalf.MinFlow = ZoneController.MinSetPoint;
     258    11686943 :             ZoneInterHalf.MinFlowCalc = false;
     259    11686943 :             ZoneInterHalf.MinFlowResult = true;
     260             :             // Record the minimum results and set flow to half way between the max and min and find results
     261    59586870 :         } else if (ZoneInterHalf.MinFlowResult) {
     262    11636955 :             ZoneInterHalf.MinResult = ZoneController.SensedValue;
     263    11636955 :             HalvingPrec = (ZoneInterHalf.MaxResult - ZoneInterHalf.MinResult) * iter_fac;
     264    11636955 :             ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     265    11636955 :             ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     266    11636955 :             ZoneInterHalf.MinFlowResult = false;
     267    11636955 :             ZoneInterHalf.NormFlowCalc = true;
     268             :             // Record the Mid results and check all possibilities and start interval halving procedure
     269    47949915 :         } else if (ZoneInterHalf.NormFlowCalc) {
     270    47949915 :             ZoneInterHalf.MidResult = ZoneController.SensedValue;
     271             : 
     272             :             // First check to see if the component is running; if not converge and return
     273    47949915 :             if (ZoneInterHalf.MaxResult == ZoneInterHalf.MinResult) {
     274             :                 // Set to converged controller
     275     1150382 :                 ZoneInterHalf.MaxFlowCalc = true;
     276     1150382 :                 ZoneInterHalf.MinFlowCalc = false;
     277     1150382 :                 ZoneInterHalf.NormFlowCalc = false;
     278     1150382 :                 ZoneInterHalf.MinFlowResult = false;
     279     1150382 :                 ZoneInterHalf.MaxResult = 1.0;
     280     1150382 :                 ZoneInterHalf.MinResult = 0.0;
     281     1150382 :                 if ((SimCompNum >= 4) && (SimCompNum <= 6)) { // hot water baseboards use min flow
     282       18719 :                     ZoneController.CalculatedSetPoint = 0.0;  // CR7253
     283             :                 } else {
     284     1131663 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow; // CR7253
     285             :                 }
     286             :                 // Set the Actuated node MassFlowRate with zero value
     287     1150382 :                 if (plantLoc.loopNum) { // this is a plant component
     288      498674 :                     PlantUtilities::SetActuatedBranchFlowRate(state,
     289      498674 :                                                               ZoneController.CalculatedSetPoint,
     290             :                                                               ActuatedNode,
     291             :                                                               plantLoc,
     292             :                                                               false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     293             :                 } else {                                              // assume not a plant component
     294      651708 :                     state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     295             :                 }
     296     1150382 :                 return;
     297             :             }
     298             : 
     299             :             // The next series of checks is to determine what interval the current solution is in
     300             :             //   comparison to the setpoint and then respond appropriately.
     301             : 
     302             :             // Normal controller assumes that MaxResult will be greater than MinResult. First check
     303             :             // to make sure that this is the case
     304    46799533 :             if (ZoneInterHalf.MaxResult <= ZoneInterHalf.MinResult) {
     305      138212 :                 if (WaterCoilAirFlowControl) {
     306      138169 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     307             :                 } else {
     308          43 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     309             :                 }
     310             :                 // set to converged controller
     311      138212 :                 Converged = true;
     312      138212 :                 ZoneInterHalf.MaxFlowCalc = true;
     313      138212 :                 ZoneInterHalf.MinFlowCalc = false;
     314      138212 :                 ZoneInterHalf.NormFlowCalc = false;
     315      138212 :                 ZoneInterHalf.MinFlowResult = false;
     316      138212 :                 ZoneInterHalf.MaxResult = 1.0;
     317      138212 :                 ZoneInterHalf.MinResult = 0.0;
     318             :                 // MaxResult is greater than MinResult so simulation control algorithm may proceed normally
     319             :             } else {
     320             :                 // Now check to see if the setpoint is outside the endpoints of the control range
     321             :                 // First check to see if the water is too cold and if so set to the minimum flow.
     322    46661321 :                 if (ZoneController.SetPoint <= ZoneInterHalf.MinResult) {
     323        6995 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     324             :                     // Set to Converged Controller
     325        6995 :                     Converged = true;
     326        6995 :                     ZoneInterHalf.MaxFlowCalc = true;
     327        6995 :                     ZoneInterHalf.MinFlowCalc = false;
     328        6995 :                     ZoneInterHalf.NormFlowCalc = false;
     329        6995 :                     ZoneInterHalf.MinFlowResult = false;
     330        6995 :                     ZoneInterHalf.MaxResult = 1.0;
     331        6995 :                     ZoneInterHalf.MinResult = 0.0;
     332             :                     // Then check if too hot and if so set it to the maximum flow
     333    46654326 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MaxResult) {
     334     4198982 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     335             :                     // Set to Converged Controller
     336     4198982 :                     Converged = true;
     337     4198982 :                     ZoneInterHalf.MaxFlowCalc = true;
     338     4198982 :                     ZoneInterHalf.MinFlowCalc = false;
     339     4198982 :                     ZoneInterHalf.NormFlowCalc = false;
     340     4198982 :                     ZoneInterHalf.MinFlowResult = false;
     341     4198982 :                     ZoneInterHalf.MaxResult = 1.0;
     342     4198982 :                     ZoneInterHalf.MinResult = 0.0;
     343             :                     // If between the max and mid set to new flow and raise min to mid
     344    42455344 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MidResult) {
     345    18425197 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     346    18425197 :                     ZoneInterHalf.MinFlow = ZoneInterHalf.MidFlow;
     347    18425197 :                     ZoneInterHalf.MinResult = ZoneInterHalf.MidResult;
     348    18425197 :                     ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     349             :                     // If between the min and mid set to new flow and lower Max to mid
     350             :                 } else {
     351    24030147 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     352    24030147 :                     ZoneInterHalf.MaxFlow = ZoneInterHalf.MidFlow;
     353    24030147 :                     ZoneInterHalf.MaxResult = ZoneInterHalf.MidResult;
     354    24030147 :                     ZoneInterHalf.MidFlow = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     355             : 
     356             :                 } // End of the Conditional for the actual interval halving scheme itself
     357             :             }     // end of max > min check
     358             : 
     359             :         } // End of the Conditinal for the first 3 iterations for the interval halving
     360             : 
     361             :         // Make sure that the Calculated setpoint falls between the minimum and maximum allowed
     362    81909173 :         if (ZoneController.CalculatedSetPoint > ZoneController.MaxSetPoint) {
     363           0 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     364           0 :             Converged = true;
     365           0 :             ZoneInterHalf.MaxFlowCalc = true;
     366           0 :             ZoneInterHalf.MinFlowCalc = false;
     367           0 :             ZoneInterHalf.NormFlowCalc = false;
     368           0 :             ZoneInterHalf.MinFlowResult = false;
     369           0 :             ZoneInterHalf.MaxResult = 1.0;
     370           0 :             ZoneInterHalf.MinResult = 0.0;
     371    81909173 :         } else if (ZoneController.CalculatedSetPoint < ZoneController.MinSetPoint) {
     372          17 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     373          17 :             Converged = true;
     374          17 :             ZoneInterHalf.MaxFlowCalc = true;
     375          17 :             ZoneInterHalf.MinFlowCalc = false;
     376          17 :             ZoneInterHalf.NormFlowCalc = false;
     377          17 :             ZoneInterHalf.MinFlowResult = false;
     378          17 :             ZoneInterHalf.MaxResult = 1.0;
     379          17 :             ZoneInterHalf.MinResult = 0.0;
     380             :         }
     381             : 
     382             :         // check if hunting down around the limit of a significant mass flow in systems.
     383    81909173 :         if ((Iter > MaxIter / 2) && (ZoneController.CalculatedSetPoint < DataBranchAirLoopPlant::MassFlowTolerance)) {
     384           0 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     385           0 :             Converged = true;
     386           0 :             ZoneInterHalf.MaxFlowCalc = true;
     387           0 :             ZoneInterHalf.MinFlowCalc = false;
     388           0 :             ZoneInterHalf.NormFlowCalc = false;
     389           0 :             ZoneInterHalf.MinFlowResult = false;
     390           0 :             ZoneInterHalf.MaxResult = 1.0;
     391           0 :             ZoneInterHalf.MinResult = 0.0;
     392             :         }
     393             : 
     394             :         // Set the Actuated node MassFlowRate with the new value
     395    81909173 :         if (plantLoc.loopNum) { // this is a plant component
     396    76529801 :             PlantUtilities::SetActuatedBranchFlowRate(state,
     397    76529801 :                                                       ZoneController.CalculatedSetPoint,
     398             :                                                       ActuatedNode,
     399             :                                                       plantLoc,
     400             :                                                       false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     401             :         } else {                                              // assume not a plant component, leave alone
     402     5379372 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     403             :         }
     404             : 
     405             :         // The denominator of the control signal should be no less than 100 watts
     406    81909173 :         Real64 Denom = sign(max(std::abs(QZnReq), 100.0), QZnReq);
     407    81909173 :         if (present(Action)) {
     408      753148 :             if (Action == iNormalAction) {
     409      530739 :                 Denom = max(std::abs(QZnReq), 100.0);
     410      222409 :             } else if (Action == iReverseAction) {
     411      222409 :                 Denom = -max(std::abs(QZnReq), 100.0);
     412             :             } else {
     413           0 :                 ShowFatalError(state, format("ControlCompOutput: Illegal Action argument =[{}]", Action));
     414             :             }
     415             :         }
     416             : 
     417    81909173 :         switch (SimCompNum) {      // Tuned If block changed to switch
     418      158452 :         case ParallelPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT'
     419             :             // simulate series piu reheat coil
     420      158452 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     421             :             // Calculate the control signal (the variable we are forcing to zero)
     422      158452 :             CpAir = Psychrometrics::PsyCpAirFnW(
     423      158452 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     424      158452 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     425      158452 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     426      158452 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     427      158452 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     428      158452 :             break;
     429             : 
     430      963794 :         case SeriesPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT'
     431             :             // simulate series piu reheat coil
     432      963794 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     433             :             // Calculate the control signal (the variable we are forcing to zero)
     434      963794 :             CpAir = Psychrometrics::PsyCpAirFnW(
     435      963794 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     436      963794 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     437      963794 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     438      963794 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     439      963794 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     440      963794 :             break;
     441             : 
     442    78184195 :         case HeatingCoilWaterNum: // 'COIL:HEATING:WATER'
     443             :             // Simulate reheat coil for the VAV system
     444    78184195 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     445             :             // Calculate the control signal (the variable we are forcing to zero)
     446    78184195 :             CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat);
     447    78184195 :             if (present(AirMassFlow)) {
     448    72804823 :                 LoadMet = AirMassFlow * CpAir * state.dataLoopNodes->Node(TempOutNode).Temp;
     449    72804823 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     450             :             } else {
     451     5379372 :                 WaterCoilAirFlowControl = true;
     452     5379372 :                 LoadMet = state.dataLoopNodes->Node(TempOutNode).MassFlowRate * CpAir *
     453     5379372 :                           (state.dataLoopNodes->Node(TempOutNode).Temp -
     454     5379372 :                            state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     455     5379372 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     456             :             }
     457    78184195 :             break;
     458             : 
     459      728696 :         case BBWaterConvOnlyNum: // 'ZONEHVAC:BASEBOARD:CONVECTIVE:WATER'
     460             :             // Simulate baseboard
     461      728696 :             BaseboardRadiator::SimHWConvective(state, CompNum, LoadMet);
     462             :             // Calculate the control signal (the variable we are forcing to zero)
     463      728696 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     464      728696 :             break;
     465             : 
     466       27961 :         case BBSteamRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM'
     467             :             // Simulate baseboard
     468       27961 :             SteamBaseboardRadiator::CalcSteamBaseboard(state, CompNum, LoadMet);
     469             :             // Calculate the control signal (the variable we are forcing to zero)
     470       27961 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     471       27961 :             break;
     472             : 
     473      160405 :         case BBWaterRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER'
     474             :             // Simulate baseboard
     475      160405 :             HWBaseboardRadiator::CalcHWBaseboard(state, CompNum, LoadMet);
     476             :             // Calculate the control signal (the variable we are forcing to zero)
     477      160405 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     478      160405 :             break;
     479             : 
     480           0 :         case FourPipeFanCoilNum: // 'ZONEHVAC:FOURPIPEFANCOIL'
     481             :             // Simulate fancoil unit
     482           0 :             FanCoilUnits::Calc4PipeFanCoil(state, CompNum, ControlledZoneIndex, FirstHVACIteration, LoadMet);
     483             :             // Calculate the control signal (the variable we are forcing to zero)
     484           0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     485           0 :             break;
     486             : 
     487      753148 :         case OutdoorAirUnitNum: //'ZONEHVAC:OUTDOORAIRUNIT'
     488             :             // Simulate outdoor air unit components
     489      753148 :             OutdoorAirUnit::CalcOAUnitCoilComps(
     490             :                 state, CompNum, FirstHVACIteration, EquipIndex, LoadMet); // Autodesk:OPTIONAL EquipIndex used without PRESENT check
     491             :             // Calculate the control signal (the variable we are forcing to zero)
     492      753148 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     493      753148 :             break;
     494             : 
     495      162775 :         case UnitHeaterNum: // 'ZONEHVAC:UNITHEATER'
     496             :             // Simulate unit heater components
     497      162775 :             UnitHeater::CalcUnitHeaterComponents(state, CompNum, FirstHVACIteration, LoadMet);
     498             :             // Calculate the control signal (the variable we are forcing to zero)
     499      162775 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     500      162775 :             break;
     501             : 
     502      342677 :         case UnitVentilatorNum: // 'ZONEHVAC:UNITVENTILATOR'
     503             :             // Simulate unit ventilator components
     504      342677 :             UnitVentilator::CalcUnitVentilatorComponents(state, CompNum, FirstHVACIteration, LoadMet);
     505             :             // Calculate the control signal (the variable we are forcing to zero)
     506      342677 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     507      342677 :             break;
     508             : 
     509      427070 :         case VentilatedSlabNum: // 'ZONEHVAC:VENTILATEDSLAB'
     510             :             // Simulate unit ventilator components
     511      427070 :             VentilatedSlab::CalcVentilatedSlabComps(state, CompNum, FirstHVACIteration, LoadMet);
     512             :             // Calculate the control signal (the variable we are forcing to zero)
     513      427070 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     514      427070 :             break;
     515             : 
     516           0 :         default:
     517           0 :             ShowFatalError(state, format("ControlCompOutput: Illegal Component Number argument =[{}]", SimCompNum));
     518           0 :             break;
     519             :         }
     520             : 
     521             :         // Check for Controller convergence to see if within the offset
     522    81909173 :         if (std::abs(ZoneController.SensedValue) <= ControlOffset || std::abs(ZoneController.SensedValue) <= HalvingPrec) {
     523             :             // Set to converged controller
     524     6225613 :             ZoneInterHalf.MaxFlowCalc = true;
     525     6225613 :             ZoneInterHalf.MinFlowCalc = false;
     526     6225613 :             ZoneInterHalf.NormFlowCalc = false;
     527     6225613 :             ZoneInterHalf.MinFlowResult = false;
     528     6225613 :             ZoneInterHalf.MaxResult = 1.0;
     529     6225613 :             ZoneInterHalf.MinResult = 0.0;
     530     6225613 :             break;
     531             :         }
     532    75683560 :         if (!Converged) {
     533    71339354 :             bool BBConvergeCheckFlag = BBConvergeCheck(SimCompNum, ZoneInterHalf.MaxFlow, ZoneInterHalf.MinFlow);
     534    71339354 :             if (BBConvergeCheckFlag) {
     535             :                 // Set to converged controller
     536       65541 :                 ZoneInterHalf.MaxFlowCalc = true;
     537       65541 :                 ZoneInterHalf.MinFlowCalc = false;
     538       65541 :                 ZoneInterHalf.NormFlowCalc = false;
     539       65541 :                 ZoneInterHalf.MinFlowResult = false;
     540       65541 :                 ZoneInterHalf.MaxResult = 1.0;
     541       65541 :                 ZoneInterHalf.MinResult = 0.0;
     542       65541 :                 break;
     543             :             }
     544             :         }
     545             : 
     546    75618019 :         ++Iter;
     547    75618019 :         if ((Iter > MaxIter) && (!state.dataGlobal->WarmupFlag)) {
     548             :             // if ( CompErrIndex == 0 ) {
     549           0 :             ShowWarningMessage(state, format("ControlCompOutput: Maximum iterations exceeded for {} = {}", CompType, CompName));
     550           0 :             ShowContinueError(state, format("... Load met       = {:.5T} W.", LoadMet));
     551           0 :             ShowContinueError(state, format("... Load requested = {:.5T} W.", QZnReq));
     552           0 :             ShowContinueError(state, format("... Error          = {:.8T} %.", std::abs((LoadMet - QZnReq) * 100.0 / Denom)));
     553           0 :             ShowContinueError(state, format("... Tolerance      = {:.8T} %.", ControlOffset * 100.0));
     554           0 :             ShowContinueError(state, "... Error          = (Load met - Load requested) / MAXIMUM(Load requested, 100)");
     555           0 :             ShowContinueError(state, format("... Actuated Node Mass Flow Rate ={:.9R} kg/s", state.dataLoopNodes->Node(ActuatedNode).MassFlowRate));
     556           0 :             ShowContinueErrorTimeStamp(state, "");
     557           0 :             ShowRecurringWarningErrorAtEnd(state,
     558           0 :                                            "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
     559             :                                            CompErrIndex,
     560           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     561           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     562             :                                            _,
     563             :                                            "%",
     564             :                                            "%");
     565             :             //}
     566           0 :             ShowRecurringWarningErrorAtEnd(state,
     567           0 :                                            "ControlCompOutput: Maximum iterations error for " + CompType + " = " + CompName,
     568             :                                            CompErrIndex,
     569           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     570           0 :                                            std::abs((LoadMet - QZnReq) * 100.0 / Denom),
     571             :                                            _,
     572             :                                            "%",
     573             :                                            "%");
     574           0 :             break; // It will not converge this time
     575    75618019 :         } else if (Iter > MaxIter * 2) {
     576           0 :             break;
     577             :         }
     578             : 
     579             :     } // End of the Convergence Iteration
     580             : }
     581             : 
     582    71339354 : bool BBConvergeCheck(int const SimCompNum, Real64 const MaxFlow, Real64 const MinFlow)
     583             : {
     584             : 
     585             :     // FUNCTION INFORMATION:
     586             :     //       AUTHOR         Rick Strand
     587             :     //       DATE WRITTEN   November 2017
     588             : 
     589             :     // PURPOSE OF THIS SUBROUTINE:
     590             :     // This is an additional check for the radiant/convective baseboard units
     591             :     // to see if they are converged or the flow is sufficiently converged to
     592             :     // procede with the simulation.  With the radiant component to these systems,
     593             :     // the impact on the load met is more difficult to calculate and the impact
     594             :     // on the actual system output is not as well behaved as for convective
     595             :     // systems.  This additional check avoids excessive iterations and max
     596             :     // iteration warnings and provides sufficiently converged results.  It is
     597             :     // only called from ControlCompOutput.
     598             : 
     599             :     // Return Value
     600             :     bool BBConvergeCheck;
     601             : 
     602             :     // SUBROUTINE PARAMETER DEFINITIONS:
     603             :     static Real64 constexpr BBIterLimit = 0.00001;
     604             : 
     605    71339354 :     if (SimCompNum != BBSteamRadConvNum && SimCompNum != BBWaterRadConvNum) {
     606             :         // For all zone equipment except radiant/convective baseboard (steam and water) units:
     607    71159565 :         BBConvergeCheck = false;
     608             :     } else {
     609             :         // For steam and water radiant/convective baseboard units:
     610      179789 :         if ((MaxFlow - MinFlow) > BBIterLimit) {
     611      114248 :             BBConvergeCheck = false;
     612             :         } else {
     613       65541 :             BBConvergeCheck = true;
     614             :         }
     615             :     }
     616             : 
     617    71339354 :     return BBConvergeCheck;
     618             : }
     619             : 
     620        6884 : void CheckSysSizing(EnergyPlusData &state,
     621             :                     std::string_view const CompType, // Component Type (e.g. Chiller:Electric)
     622             :                     std::string const &CompName      // Component Name (e.g. Big Chiller)
     623             : )
     624             : {
     625             : 
     626             :     // SUBROUTINE INFORMATION:
     627             :     //       AUTHOR         Fred Buhl
     628             :     //       DATE WRITTEN   October 2002
     629             : 
     630             :     // PURPOSE OF THIS SUBROUTINE:
     631             :     // This routine is called when an "autosize" input is encountered in a component
     632             :     // sizing routine to check that the system sizing calculations have been done.
     633             : 
     634             :     // METHODOLOGY EMPLOYED:
     635             :     // Checks SysSizingRunDone flag. If false throws a fatal error.
     636             : 
     637        6884 :     if (!state.dataSize->SysSizingRunDone) {
     638           0 :         ShowSevereError(state, format("For autosizing of {} {}, a system sizing run must be done.", CompType, CompName));
     639           0 :         if (state.dataSize->NumSysSizInput == 0) {
     640           0 :             ShowContinueError(state, "No \"Sizing:System\" objects were entered.");
     641             :         }
     642           0 :         if (!state.dataGlobal->DoSystemSizing) {
     643           0 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do System Sizing Calculation" set to Yes.)");
     644             :         }
     645           0 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     646             :     }
     647        6884 : }
     648             : 
     649        5052 : void CheckThisAirSystemForSizing(EnergyPlusData &state, int const AirLoopNum, bool &AirLoopWasSized)
     650             : {
     651             : 
     652             :     // SUBROUTINE INFORMATION:
     653             :     //       AUTHOR         B. Griffith
     654             :     //       DATE WRITTEN   October 2013
     655             : 
     656        5052 :     AirLoopWasSized = false;
     657        5052 :     if (state.dataSize->SysSizingRunDone) {
     658       25422 :         for (int ThisAirSysSizineInputLoop = 1; ThisAirSysSizineInputLoop <= state.dataSize->NumSysSizInput; ++ThisAirSysSizineInputLoop) {
     659       25414 :             if (state.dataSize->SysSizInput(ThisAirSysSizineInputLoop).AirLoopNum == AirLoopNum) {
     660        4693 :                 AirLoopWasSized = true;
     661        4693 :                 break;
     662             :             }
     663             :         }
     664             :     }
     665        5052 : }
     666             : 
     667       13583 : void CheckZoneSizing(EnergyPlusData &state,
     668             :                      std::string_view const CompType, // Component Type (e.g. Chiller:Electric)
     669             :                      std::string_view const CompName  // Component Name (e.g. Big Chiller)
     670             : )
     671             : {
     672             : 
     673             :     // SUBROUTINE INFORMATION:
     674             :     //       AUTHOR         Fred Buhl
     675             :     //       DATE WRITTEN   October 2002
     676             : 
     677             :     // PURPOSE OF THIS SUBROUTINE:
     678             :     // This routine is called when an "autosize" input is encountered in a component
     679             :     // sizing routine to check that the zone sizing calculations have been done.
     680             : 
     681             :     // METHODOLOGY EMPLOYED:
     682             :     // Checks ZoneSizingRunDone flag. If false throws a fatal error.
     683             : 
     684       13583 :     if (!state.dataSize->ZoneSizingRunDone) {
     685           0 :         ShowSevereError(state, format("For autosizing of {} {}, a zone sizing run must be done.", CompType, CompName));
     686           0 :         if (state.dataSize->NumZoneSizingInput == 0) {
     687           0 :             ShowContinueError(state, "No \"Sizing:Zone\" objects were entered.");
     688             :         }
     689           0 :         if (!state.dataGlobal->DoZoneSizing) {
     690           0 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do Zone Sizing Calculation" set to Yes.)");
     691             :         }
     692           0 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     693             :     }
     694       13583 : }
     695             : 
     696         638 : void CheckThisZoneForSizing(EnergyPlusData &state,
     697             :                             int const ZoneNum, // zone index to be checked
     698             :                             bool &ZoneWasSized)
     699             : {
     700             : 
     701             :     // SUBROUTINE INFORMATION:
     702             :     //       AUTHOR         B. Griffith
     703             :     //       DATE WRITTEN   Oct 2013
     704             : 
     705             :     // PURPOSE OF THIS SUBROUTINE:
     706             :     // utility routine to see if a particular zone has a Sizing:Zone object for it
     707             :     // and that sizing was done.
     708             : 
     709         638 :     ZoneWasSized = false;
     710         638 :     if (state.dataSize->ZoneSizingRunDone) {
     711        8442 :         for (int ThisSizingInput = 1; ThisSizingInput <= state.dataSize->NumZoneSizingInput; ++ThisSizingInput) {
     712        8442 :             if (state.dataSize->ZoneSizingInput(ThisSizingInput).ZoneNum == ZoneNum) {
     713         632 :                 ZoneWasSized = true;
     714         632 :                 break;
     715             :             }
     716             :         }
     717             :     }
     718         638 : }
     719             : 
     720       38842 : void ValidateComponent(EnergyPlusData &state,
     721             :                        std::string_view CompType,   // Component Type (e.g. Chiller:Electric)
     722             :                        std::string const &CompName, // Component Name (e.g. Big Chiller)
     723             :                        bool &IsNotOK,               // .TRUE. if this component pair is invalid
     724             :                        std::string_view CallString  // Context of this pair -- for error message
     725             : )
     726             : {
     727             : 
     728             :     // SUBROUTINE INFORMATION:
     729             :     //       AUTHOR         Linda Lawrie
     730             :     //       DATE WRITTEN   October 2002
     731             : 
     732             :     // PURPOSE OF THIS SUBROUTINE:
     733             :     // This subroutine can be called to validate the component type-name pairs that
     734             :     // are so much a part of the EnergyPlus input.  The main drawback to this validation
     735             :     // has been that the "GetInput" routine may not have been called and/or exists in
     736             :     // another module from the one with the list.  This means that validation must be
     737             :     // done later, perhaps after simulation has already started or perhaps raises an
     738             :     // array bound error instead.
     739             : 
     740             :     // METHODOLOGY EMPLOYED:
     741             :     // Uses existing routines in InputProcessor.  GetObjectItemNum uses the "standard"
     742             :     // convention of the Name of the item/object being the first Alpha Argument.
     743             : 
     744       38842 :     IsNotOK = false;
     745             : 
     746       38842 :     int ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, std::string{CompType}, CompName);
     747             : 
     748       38842 :     if (ItemNum < 0) {
     749           0 :         ShowSevereError(state, format("During {} Input, Invalid Component Type input={}", CallString, CompType));
     750           0 :         ShowContinueError(state, format("Component name={}", CompName));
     751           0 :         IsNotOK = true;
     752       38842 :     } else if (ItemNum == 0) {
     753           0 :         ShowSevereError(state, format("During {} Input, Invalid Component Name input={}", CallString, CompName));
     754           0 :         ShowContinueError(state, format("Component type={}", CompType));
     755           0 :         IsNotOK = true;
     756             :     }
     757       38842 : }
     758             : 
     759          15 : void ValidateComponent(EnergyPlusData &state,
     760             :                        std::string_view CompType,      // Component Type (e.g. Chiller:Electric)
     761             :                        std::string const &CompValType, // Component "name" field type
     762             :                        std::string const &CompName,    // Component Name (e.g. Big Chiller)
     763             :                        bool &IsNotOK,                  // .TRUE. if this component pair is invalid
     764             :                        std::string_view CallString     // Context of this pair -- for error message
     765             : )
     766             : {
     767             : 
     768             :     // SUBROUTINE INFORMATION:
     769             :     //       AUTHOR         Linda Lawrie
     770             :     //       DATE WRITTEN   October 2002
     771             : 
     772             :     // PURPOSE OF THIS SUBROUTINE:
     773             :     // This subroutine can be called to validate the component type-name pairs that
     774             :     // are so much a part of the EnergyPlus input.  The main drawback to this validation
     775             :     // has been that the "GetInput" routine may not have been called and/or exists in
     776             :     // another module from the one with the list.  This means that validation must be
     777             :     // done later, perhaps after simulation has already started or perhaps raises an
     778             :     // array bound error instead.
     779             : 
     780             :     // METHODOLOGY EMPLOYED:
     781             :     // Uses existing routines in InputProcessor.  GetObjectItemNum uses the "standard"
     782             :     // convention of the Name of the item/object being the first Alpha Argument.
     783             : 
     784          15 :     IsNotOK = false;
     785             : 
     786          15 :     int ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, CompType, CompValType, CompName);
     787             : 
     788          15 :     if (ItemNum < 0) {
     789           0 :         ShowSevereError(state, format("During {} Input, Invalid Component Type input={}", CallString, CompType));
     790           0 :         ShowContinueError(state, format("Component name={}", CompName));
     791           0 :         IsNotOK = true;
     792          15 :     } else if (ItemNum == 0) {
     793           0 :         ShowSevereError(state, format("During {} Input, Invalid Component Name input={}", CallString, CompName));
     794           0 :         ShowContinueError(state, format("Component type={}", CompType));
     795           0 :         IsNotOK = true;
     796             :     }
     797          15 : }
     798             : 
     799     4880276 : void CalcBasinHeaterPower(EnergyPlusData &state,
     800             :                           Real64 const Capacity,     // Basin heater capacity per degree C below setpoint (W/C)
     801             :                           int const SchedulePtr,     // Pointer to basin heater schedule
     802             :                           Real64 const SetPointTemp, // setpoint temperature for basin heater operation (C)
     803             :                           Real64 &Power              // Basin heater power (W)
     804             : )
     805             : {
     806             : 
     807             :     // SUBROUTINE INFORMATION:
     808             :     //       AUTHOR         Chandan Sharma, FSEC
     809             :     //       DATE WRITTEN   Feb 2010
     810             : 
     811             :     // PURPOSE OF THIS SUBROUTINE:
     812             :     // To calculate basin heater power when the evaporative cooled equipment is not operating
     813             :     // and outdoor air dry-bulb temperature is below the set-point
     814             : 
     815             :     // METHODOLOGY EMPLOYED:
     816             :     // Checks to see whether schedule for basin heater exists or not. If the schedule exists,
     817             :     // the basin heater is operated for the schedule specified otherwise the heater runs
     818             :     // for the entire simulation timestep whenever the outdoor temperature is below setpoint
     819             :     // and water is not flowing through the evaporative cooled equipment.
     820             : 
     821     4880276 :     Power = 0.0;
     822             :     // Operate basin heater anytime outdoor temperature is below setpoint and water is not flowing through the equipment
     823             :     // IF schedule exists, basin heater performance can be scheduled OFF
     824     4880276 :     if (SchedulePtr > 0) {
     825       23493 :         Real64 BasinHeaterSch = ScheduleManager::GetCurrentScheduleValue(state, SchedulePtr);
     826       23493 :         if (Capacity > 0.0 && BasinHeaterSch > 0.0) {
     827       23487 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
     828             :         }
     829             :     } else {
     830             :         // IF schedule does not exist, basin heater operates anytime outdoor dry-bulb temp is below setpoint
     831     4856783 :         if (Capacity > 0.0) {
     832      192589 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
     833             :         }
     834             :     }
     835     4880276 : }
     836             : 
     837         795 : void TestAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
     838             : {
     839             : 
     840             :     // SUBROUTINE INFORMATION:
     841             :     //       AUTHOR         Linda Lawrie
     842             :     //       DATE WRITTEN   March 2003
     843             : 
     844             :     // PURPOSE OF THIS SUBROUTINE:
     845             :     // This subroutine tests supply, return and overall air path integrity.
     846             : 
     847             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     848             :     bool errFlag;
     849         795 :     Array2D_int ValRetAPaths;
     850         795 :     Array2D_int NumRAPNodes;
     851         795 :     Array2D_int ValSupAPaths;
     852         795 :     Array2D_int NumSAPNodes;
     853             : 
     854         795 :     NumSAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     855         795 :     NumRAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     856         795 :     ValRetAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     857         795 :     ValSupAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     858         795 :     NumSAPNodes = 0;
     859         795 :     NumRAPNodes = 0;
     860         795 :     ValRetAPaths = 0;
     861         795 :     ValSupAPaths = 0;
     862             : 
     863         795 :     TestSupplyAirPathIntegrity(state, errFlag);
     864         795 :     if (errFlag) ErrFound = true;
     865         795 :     TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
     866         795 :     if (errFlag) ErrFound = true;
     867             : 
     868             :     // Final tests, look for duplicate nodes
     869        2002 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     870        1207 :         if (ValRetAPaths(1, Loop) != 0) continue;
     871          13 :         if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) continue;
     872          13 :         ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
     873             :     }
     874             : 
     875        2002 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     876      363250 :         for (int Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
     877      362043 :             int TestNode = ValRetAPaths(Loop1, Loop);
     878      362043 :             int Count = 0;
     879    15315356 :             for (int Loop2 = 1; Loop2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop2) {
     880    49072819 :                 for (int Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
     881    49072819 :                     if (Loop2 == Loop && Loop1 == Loop3) continue; // Don't count test node
     882    49066664 :                     if (ValRetAPaths(Loop3, Loop2) == 0) break;
     883    34113351 :                     if (ValRetAPaths(Loop3, Loop2) == TestNode) ++Count;
     884             :                 }
     885             :             }
     886      362043 :             if (Count > 0) {
     887           0 :                 ShowSevereError(state, "Duplicate Node detected in Return Air Paths");
     888           0 :                 ShowContinueError(state, format("Test Node={}", state.dataLoopNodes->NodeID(TestNode)));
     889           0 :                 ShowContinueError(state, format("In Air Path={}", state.dataAirLoop->AirToZoneNodeInfo(Loop).AirLoopName));
     890           0 :                 ErrFound = true;
     891             :             }
     892             :         }
     893             :     }
     894             : 
     895         795 :     NumSAPNodes.deallocate();
     896         795 :     NumRAPNodes.deallocate();
     897         795 :     ValRetAPaths.deallocate();
     898         795 :     ValSupAPaths.deallocate();
     899         795 : }
     900             : 
     901         795 : void TestSupplyAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
     902             : {
     903             : 
     904             :     // SUBROUTINE INFORMATION:
     905             :     //       AUTHOR         Linda Lawrie
     906             :     //       DATE WRITTEN   March 2003
     907             : 
     908             :     // PURPOSE OF THIS SUBROUTINE:
     909             :     // This subroutine tests supply air path integrity and displays the loop for each branch.
     910             :     // Also, input and output nodes.
     911             : 
     912             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     913         795 :     std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
     914         795 :     Array1D_bool FoundSupplyPlenum;
     915         795 :     Array1D_bool FoundZoneSplitter;
     916         795 :     Array1D_string FoundNames;
     917         795 :     int NumErr = 0; // Error Counter //Autodesk:Init Initialization added
     918             : 
     919             :     // Do by Paths
     920         795 :     ShowMessage(state, "Testing Individual Supply Air Path Integrity");
     921         795 :     ErrFound = false;
     922             : 
     923         795 :     print(state.files.bnd, "{}\n", "! ===============================================================");
     924             :     static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
     925         795 :     print(state.files.bnd, "{}\n", Format_700);
     926         795 :     print(state.files.bnd, " #Supply Air Paths,{}\n", state.dataZoneEquip->NumSupplyAirPaths);
     927             :     static constexpr std::string_view Format_702("! <Supply Air Path>,<Supply Air Path Count>,<Supply Air Path Name>,<AirLoopHVAC Name>");
     928         795 :     print(state.files.bnd, "{}\n", Format_702);
     929             :     static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
     930         795 :     print(state.files.bnd, "{}\n", Format_703);
     931             :     static constexpr std::string_view Format_704(
     932             :         "! <Supply Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
     933         795 :     print(state.files.bnd, "{}\n", Format_704);
     934             :     static constexpr std::string_view Format_707("! <#Outlet Nodes on Supply Air Path Component>,<Number of Nodes>");
     935         795 :     print(state.files.bnd, "{}\n", Format_707);
     936             :     static constexpr std::string_view Format_708(
     937             :         "! <Supply Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
     938             :         "Node Name>,<AirLoopHVAC Name>");
     939         795 :     print(state.files.bnd, "{}\n", Format_708);
     940             : 
     941        2006 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
     942             : 
     943             :         // Determine which air loop this supply air path is connected to
     944        1211 :         int Found = 0;
     945        6542 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
     946        6542 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
     947        6542 :             Found = 0;
     948       13100 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
     949        6558 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
     950        6558 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2))
     951        1211 :                     Found = Count2;
     952             :             }
     953        6542 :             if (Found != 0) break;
     954             :         }
     955        1211 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
     956             : 
     957        1211 :         print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
     958        1211 :         print(state.files.bnd, "   #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
     959             : 
     960        1211 :         std::string AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
     961             : 
     962        2429 :         for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
     963             : 
     964        1218 :             print(state.files.bnd,
     965             :                   "   Supply Air Path Component,{},{},{},{}\n",
     966             :                   Count,
     967        1218 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     968        1218 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
     969             :                   PrimaryAirLoopName);
     970             : 
     971             :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
     972        1218 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
     973             : 
     974        1218 :             switch (CompType) {
     975           8 :             case AirLoopHVACCompType::SupplyPlenum: {
     976          20 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
     977          12 :                     if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
     978          12 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
     979           4 :                         continue;
     980           8 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode)) {
     981           0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
     982           0 :                         ShowContinueError(state,
     983           0 :                                           format("For AirLoopHVAC:SupplyPlenum={}", state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName));
     984           0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
     985           0 :                         ShowContinueError(state,
     986           0 :                                           format("Encountered node name (supply plenum)={}",
     987           0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(1))));
     988           0 :                         ErrFound = true;
     989           0 :                         ++NumErr;
     990             :                     }
     991           8 :                     print(state.files.bnd,
     992             :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
     993           8 :                           state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
     994          20 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
     995          12 :                         print(state.files.bnd,
     996             :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
     997             :                               Count1,
     998          12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     999          12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1000          12 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
    1001          12 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
    1002             :                               PrimaryAirLoopName);
    1003             :                     }
    1004             :                 }
    1005           8 :             } break;
    1006        1210 :             case AirLoopHVACCompType::ZoneSplitter: {
    1007       13098 :                 for (int Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
    1008       11888 :                     if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
    1009       11888 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
    1010       10678 :                         continue;
    1011        1210 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)) {
    1012           0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
    1013           0 :                         ShowContinueError(state,
    1014           0 :                                           format("For AirLoopHVAC:ZoneSplitter={}", state.dataSplitterComponent->SplitterCond(Count2).SplitterName));
    1015           0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
    1016           0 :                         ShowContinueError(state,
    1017           0 :                                           format("Encountered node name (zone splitter)={}",
    1018           0 :                                                  state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)));
    1019           0 :                         ErrFound = true;
    1020           0 :                         ++NumErr;
    1021             :                     }
    1022        1210 :                     print(state.files.bnd,
    1023             :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1024        1210 :                           state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
    1025        4889 :                     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
    1026        3679 :                         print(state.files.bnd,
    1027             :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1028             :                               Count1,
    1029        3679 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1030        3679 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1031        3679 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
    1032        3679 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
    1033             :                               PrimaryAirLoopName);
    1034             :                     }
    1035             :                 }
    1036        1210 :             } break;
    1037           0 :             default: {
    1038           0 :                 ShowSevereError(
    1039           0 :                     state, format("Invalid Component Type in Supply Air Path={}", state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count)));
    1040           0 :                 ErrFound = true;
    1041           0 :                 ++NumErr;
    1042           0 :             } break;
    1043             :             }
    1044             :         }
    1045             : 
    1046        1211 :         if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
    1047             :             static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
    1048        1211 :             print(state.files.bnd, "{}\n", Format_705);
    1049             :             static constexpr std::string_view Format_706("! <Supply Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1050        1211 :             print(state.files.bnd, "{}\n", Format_706);
    1051        1211 :             print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
    1052        6113 :             for (int Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
    1053        4902 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
    1054        1211 :                     print(state.files.bnd,
    1055             :                           "   Supply Air Path Node,Inlet Node,{},{},{}\n",
    1056             :                           Count2,
    1057        1211 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1058             :                           PrimaryAirLoopName);
    1059        3691 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
    1060           7 :                     print(state.files.bnd,
    1061             :                           "   Supply Air Path Node,Through Node,{},{},{}\n",
    1062             :                           Count2,
    1063           7 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1064             :                           PrimaryAirLoopName);
    1065        3684 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
    1066        3684 :                     print(state.files.bnd,
    1067             :                           "   Supply Air Path Node,Outlet Node,{},{},{}\n",
    1068             :                           Count2,
    1069        3684 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1070             :                           PrimaryAirLoopName);
    1071             :                 }
    1072             :             }
    1073             :         }
    1074        1211 :     }
    1075             : 
    1076         795 :     if (state.dataSplitterComponent->NumSplitters == 0) {
    1077         256 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
    1078           0 :             SplitterComponent::GetSplitterInput(state);
    1079             :         }
    1080             :     }
    1081         795 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1082         601 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum") > 0) {
    1083           0 :             ZonePlenum::GetZonePlenumInput(state);
    1084             :         }
    1085             :     }
    1086             : 
    1087             :     // now the reverse.  is every zone splitter and supply plenum on supply air path
    1088         795 :     FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
    1089         795 :     FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
    1090         795 :     FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
    1091         803 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1092          20 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1093          35 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1094          31 :                 if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1095           8 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM")
    1096          15 :                     continue;
    1097           8 :                 if (FoundSupplyPlenum(Count1)) {
    1098           0 :                     ShowSevereError(
    1099             :                         state,
    1100           0 :                         format("AirLoopHVAC:SupplyPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1101           0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1102           0 :                     ErrFound = true;
    1103             :                 } else {
    1104             :                     // record use
    1105           8 :                     FoundSupplyPlenum(Count1) = true;
    1106           8 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1107             :                 }
    1108             :             }
    1109             :         }
    1110             :     }
    1111         795 :     FoundNames.deallocate();
    1112         795 :     FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
    1113        2005 :     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1114       13098 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1115       23787 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1116       11899 :                 if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
    1117       25008 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1118        1210 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER")
    1119       10689 :                     continue;
    1120        1210 :                 if (FoundZoneSplitter(Count1)) {
    1121           0 :                     ShowSevereError(
    1122             :                         state,
    1123           0 :                         format("AirLoopHVAC:ZoneSplitter=\"{}\", duplicate entry.", state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1124           0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1125           0 :                     ErrFound = true;
    1126             :                 } else {
    1127             :                     // record use
    1128        1210 :                     FoundZoneSplitter(Count1) = true;
    1129        1210 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1130             :                 }
    1131             :             }
    1132             :         }
    1133             :     }
    1134         795 :     FoundNames.deallocate();
    1135             : 
    1136         795 :     if (!all(FoundSupplyPlenum)) {
    1137           0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1138           0 :             if (FoundSupplyPlenum(Count1)) continue;
    1139           0 :             ShowSevereError(state,
    1140           0 :                             format("AirLoopHVAC:SupplyPlenum=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1141           0 :                                    state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1142             :         }
    1143             :     }
    1144             : 
    1145         795 :     if (!all(FoundZoneSplitter)) {
    1146           0 :         for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1147           0 :             if (FoundZoneSplitter(Count1)) continue;
    1148           0 :             ShowSevereError(state,
    1149           0 :                             format("AirLoopHVAC:ZoneSplitter=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1150           0 :                                    state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1151             :         }
    1152             :     }
    1153             : 
    1154         795 :     FoundSupplyPlenum.deallocate();
    1155         795 :     FoundZoneSplitter.deallocate();
    1156             : 
    1157         795 :     if (ErrFound) {
    1158           0 :         ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
    1159             :     } else {
    1160         795 :         ShowMessage(state, "All Supply Air Paths passed integrity testing");
    1161             :     }
    1162         795 : }
    1163             : 
    1164         795 : void TestReturnAirPathIntegrity(EnergyPlusData &state, bool &ErrFound, Array2S_int ValRetAPaths)
    1165             : {
    1166             : 
    1167             :     // SUBROUTINE INFORMATION:
    1168             :     //       AUTHOR         Linda Lawrie
    1169             :     //       DATE WRITTEN   March 2003
    1170             : 
    1171             :     // PURPOSE OF THIS SUBROUTINE:
    1172             :     // This subroutine tests return air path integrity and displays the loop for each branch.
    1173             :     // Also, input and output nodes.
    1174             : 
    1175             :     // REFERENCES:
    1176             :     // Return Air Path Validity Rules:
    1177             :     //  Last component (zone mixer or zone return plenum) must resolve to
    1178             :     //  be the outlet node for the return air path.  Inlets to this component must be outlets from
    1179             :     //  previous components or "controlled zone outlets"?.
    1180             :     //  (though converse not true -- each outlet in previous components do not
    1181             :     //  have to be inlets on this item -- though they must be inputs somewhere in the stream).
    1182             :     //  If multiple components and no mixer, then a zone return plenums "outlet" must
    1183             :     //  be represented as an inlet on a later plenum.  i.e. some zone return plenums are
    1184             :     //  really acting as "mixers" in a sense.  These do not need to be stepwise in succession.
    1185             :     //  Same caveat for inlets from previous item.
    1186             :     //  If multiple components and mixer, then prior condition (nested plenums) is allowed as long as
    1187             :     //  those aren't duplicated as mixer inlets.  (i.e. zone rp 1 => zone rp 2 => zone mixer but
    1188             :     //  zone rp 1 outlet should not also be inlet to mixer.
    1189             :     //  Can have (nzrp -- nested zone return plenum, pzrp -- parallel zone return plenum):
    1190             :     //  nzrp 1 => nzrp 2 & pzrp 3 => zm (inlets from nzrp 2 and pzrp 3).  Or, likewise:
    1191             :     //  pzrp 1 & pzrp 2 => zm => pzrp 3 (outlets from pzrp 1/2 are inlets to zm whose outlet is an
    1192             :     //  inlet to pzrp 3 whose outlet is the outlet for the return air path.
    1193             : 
    1194             :     //  Cannot have duplicate nodes in the "inlet" stream?  (i.e. cannot have same zone feeding two independent
    1195             :     //  plenums, for example).  Similarly, Same return plenum can't be in two air loops nor as two independent
    1196             :     //  return plenums in one return air path.
    1197             : 
    1198             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1199         795 :     std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
    1200         795 :     Array1D_bool FoundReturnPlenum;
    1201         795 :     Array1D_bool FoundZoneMixer;
    1202         795 :     Array1D_string FoundNames;
    1203         795 :     Array1D_int AllNodes;
    1204             : 
    1205             :     // Formats
    1206             : 
    1207             :     // Do by Paths
    1208         795 :     ShowMessage(state, "Testing Individual Return Air Path Integrity");
    1209         795 :     ErrFound = false;
    1210         795 :     int NumErr = 0;
    1211             : 
    1212         795 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1213             :     static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
    1214         795 :     print(state.files.bnd, "{}\n", Format_700);
    1215         795 :     print(state.files.bnd, " #Return Air Paths,{}\n", state.dataZoneEquip->NumReturnAirPaths);
    1216             :     static constexpr std::string_view Format_702("! <Return Air Path>,<Return Air Path Count>,<Return Air Path Name>,<AirLoopHVAC Name>");
    1217         795 :     print(state.files.bnd, "{}\n", Format_702);
    1218             :     static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
    1219         795 :     print(state.files.bnd, "{}\n", Format_703);
    1220             :     static constexpr std::string_view Format_704(
    1221             :         "! <Return Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
    1222         795 :     print(state.files.bnd, "{}\n", Format_704);
    1223             :     static constexpr std::string_view Format_707("! <#Inlet Nodes on Return Air Path Component>,<Number of Nodes>");
    1224         795 :     print(state.files.bnd, "{}\n", Format_707);
    1225             :     static constexpr std::string_view Format_708(
    1226             :         "! <Return Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
    1227             :         "Node Name>,<AirLoopHVAC Name>");
    1228         795 :     print(state.files.bnd, "{}\n", Format_708);
    1229             : 
    1230         795 :     AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
    1231             : 
    1232        1989 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1233             :         //             Determine which air loop this supply air path is connected to
    1234        1194 :         int Found = 0;
    1235        6511 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
    1236        6511 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1237        6511 :             Found = 0;
    1238       13022 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
    1239        6511 :                 if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
    1240        6511 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2))
    1241        1194 :                     Found = Count2;
    1242             :             }
    1243        6511 :             if (Found != 0) break;
    1244             :         }
    1245        1194 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
    1246             : 
    1247        1194 :         print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
    1248             : 
    1249        1194 :         int NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
    1250        1194 :         print(state.files.bnd, "   #Components on Return Air Path,{}\n", NumComp);
    1251             : 
    1252        1194 :         std::string const &AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
    1253             : 
    1254        1194 :         int MixerCount = 0;
    1255        2503 :         for (int Count = 1; Count <= NumComp; ++Count) {
    1256        1309 :             print(state.files.bnd,
    1257             :                   "   Return Air Path Component,{},{},{},{}\n",
    1258             :                   Count,
    1259        1309 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
    1260        1309 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
    1261             :                   PrimaryAirLoopName);
    1262             : 
    1263        1309 :             if (Util::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
    1264        1026 :                 ++MixerCount;
    1265             :             }
    1266             :         }
    1267             : 
    1268        1194 :         if (MixerCount > 1) {
    1269           0 :             ShowSevereError(state, format("Too many zone mixers in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1270           0 :             ErrFound = true;
    1271           0 :             ++NumErr;
    1272           0 :             continue;
    1273             :         }
    1274             : 
    1275        1194 :         AllNodes = 0;
    1276        1194 :         int CountNodes = 0;
    1277             : 
    1278        1194 :         if (NumComp > 0) {
    1279             : 
    1280             :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1281        1194 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
    1282             : 
    1283        1194 :             switch (CompType) {
    1284        1026 :             case AirLoopHVACCompType::ZoneMixer: {
    1285       12520 :                 for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1286       11494 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1287       10468 :                         continue;
    1288             :                     // Found correct Mixer (by name), check outlet node vs. return air path outlet node
    1289        1026 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)) {
    1290           0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1291           0 :                         ShowContinueError(state, format("For Connector:Mixer={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1292           0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1293           0 :                         ShowContinueError(state,
    1294           0 :                                           format("Encountered node name (mixer)={}",
    1295           0 :                                                  state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)));
    1296           0 :                         ErrFound = true;
    1297           0 :                         ++NumErr;
    1298             :                     } else {
    1299        1026 :                         ++CountNodes;
    1300        1026 :                         AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
    1301        1026 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataMixerComponent->MixerCond(Count2).OutletNode) {
    1302        1026 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1303             :                         }
    1304        3461 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1305        2435 :                             ++CountNodes;
    1306        2435 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1307             :                         }
    1308             :                     }
    1309        1026 :                     print(state.files.bnd,
    1310             :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1311        1026 :                           state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
    1312        3461 :                     for (int Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
    1313        2435 :                         print(state.files.bnd,
    1314             :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1315             :                               Count1,
    1316        2435 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1317        2435 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1318        2435 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
    1319        2435 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
    1320             :                               PrimaryAirLoopName);
    1321             :                     }
    1322             :                 }
    1323        1026 :             } break;
    1324         168 :             case AirLoopHVACCompType::ReturnPlenum: {
    1325         518 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1326         350 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
    1327         350 :                         state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1328         182 :                         continue;
    1329         168 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)) {
    1330           0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1331           0 :                         ShowContinueError(
    1332           0 :                             state, format("For AirLoopHVAC:ReturnPlenum={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1333           0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1334           0 :                         ShowContinueError(state,
    1335           0 :                                           format("Encountered node name (zone return plenum)={}",
    1336           0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)));
    1337           0 :                         ErrFound = true;
    1338           0 :                         ++NumErr;
    1339             :                     } else {
    1340         168 :                         ++CountNodes;
    1341         168 :                         AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
    1342         168 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode) {
    1343         168 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1344             :                         }
    1345         918 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1346         750 :                             ++CountNodes;
    1347         750 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1348             :                         }
    1349             :                     }
    1350         168 :                     print(state.files.bnd,
    1351             :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1352         168 :                           state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
    1353         918 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
    1354         750 :                         print(state.files.bnd,
    1355             :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1356             :                               Count1,
    1357         750 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1358         750 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1359         750 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
    1360         750 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
    1361             :                               PrimaryAirLoopName);
    1362             :                     }
    1363             :                 }
    1364         168 :             } break;
    1365           0 :             default: // This already validated in GetReturnAirPath
    1366           0 :                 break;
    1367             :             }
    1368             :         }
    1369             : 
    1370        1194 :         if (NumComp > 1) {
    1371         229 :             for (int Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
    1372             : 
    1373             :                 AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1374         115 :                     getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
    1375             : 
    1376         115 :                 switch (CompType) {
    1377           0 :                 case AirLoopHVACCompType::ZoneMixer: {
    1378           0 :                     for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1379           0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1380           0 :                             continue;
    1381           0 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1382           0 :                             ++CountNodes;
    1383           0 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1384             :                         }
    1385             :                     }
    1386           0 :                 } break;
    1387         115 :                 case AirLoopHVACCompType::ReturnPlenum: {
    1388         454 :                     for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1389         339 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1390         339 :                             state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1391         224 :                             continue;
    1392         675 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1393         560 :                             ++CountNodes;
    1394         560 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1395             :                         }
    1396             :                     }
    1397         115 :                 } break;
    1398           0 :                 default: // This already validated in GetReturnAirPath
    1399           0 :                     break;
    1400             :                 }
    1401             :             }
    1402             :         }
    1403        1194 :         if (CountNodes > 0) {
    1404             :             static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
    1405        1194 :             print(state.files.bnd, "{}\n", Format_705);
    1406             :             static constexpr std::string_view Format_706("! <Return Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1407        1194 :             print(state.files.bnd, "{}\n", Format_706);
    1408        1194 :             print(state.files.bnd, "   #Nodes on Return Air Path,{}\n", CountNodes);
    1409        6133 :             for (int Count2 = 1; Count2 <= CountNodes; ++Count2) {
    1410        4939 :                 if (Count2 == 1) {
    1411        1194 :                     print(state.files.bnd,
    1412             :                           "   Return Air Path Node,Outlet Node,{},{},{}\n",
    1413             :                           Count2,
    1414        1194 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1415             :                           PrimaryAirLoopName);
    1416             :                 } else {
    1417        3745 :                     print(state.files.bnd,
    1418             :                           "   Return Air Path Node,Inlet Node,{},{},{}\n",
    1419             :                           Count2,
    1420        3745 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1421             :                           PrimaryAirLoopName);
    1422             :                 }
    1423             :             }
    1424             :         }
    1425             :         // Determine Air Loop this Return Air Path is on
    1426        6511 :         for (int Count2 = 1; Count2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count2) {
    1427        6511 :             if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
    1428        6511 :                 if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
    1429        1194 :                     const int WAirLoop = Count2;
    1430        1194 :                     ValRetAPaths(_, WAirLoop) = 0;
    1431        1194 :                     ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
    1432        1194 :                     break;
    1433             :                 }
    1434             :             } else {
    1435           0 :                 ShowWarningError(state,
    1436           0 :                                  format("TestReturnAirPathIntegrity: Air Loop has no Zone Equipment Return Node={}",
    1437           0 :                                         state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName));
    1438             :             }
    1439             :         }
    1440             :     }
    1441             : 
    1442         795 :     AllNodes.deallocate();
    1443             : 
    1444         795 :     if (state.dataMixerComponent->NumMixers == 0) {
    1445         389 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
    1446           0 :             MixerComponent::GetMixerInput(state);
    1447             :         }
    1448             :     }
    1449         795 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1450         601 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum") > 0) {
    1451           0 :             ZonePlenum::GetZonePlenumInput(state);
    1452             :         }
    1453             :     }
    1454             : 
    1455             :     // now the reverse.  is every zone Mixer and Return plenum on Return air path
    1456         795 :     FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
    1457         795 :     FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
    1458         795 :     FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
    1459        1079 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1460        1102 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1461        1975 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1462        1440 :                 if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1463         283 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM")
    1464         874 :                     continue;
    1465         283 :                 if (FoundReturnPlenum(Count1)) {
    1466           0 :                     ShowSevereError(
    1467             :                         state,
    1468           0 :                         format("AirLoopHVAC:ReturnPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1469           0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1470           0 :                     ErrFound = true;
    1471             :                 } else {
    1472             :                     // record use
    1473         283 :                     FoundReturnPlenum(Count1) = true;
    1474         283 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1475             :                 }
    1476             :             }
    1477             :         }
    1478         284 :         if (PurchasedAirManager::CheckPurchasedAirForReturnPlenum(state, Count1)) FoundReturnPlenum(Count1) = true;
    1479             :     }
    1480         795 :     FoundNames.deallocate();
    1481         795 :     FoundNames.allocate(state.dataMixerComponent->NumMixers);
    1482        1884 :     for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1483       12630 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1484       23590 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1485       13075 :                 if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1486        1026 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER")
    1487       11023 :                     continue;
    1488        1026 :                 if (FoundZoneMixer(Count1)) {
    1489           0 :                     ShowSevereError(state,
    1490           0 :                                     format("AirLoopHVAC:ZoneMixer=\"{}\", duplicate entry.", state.dataMixerComponent->MixerCond(Count1).MixerName));
    1491           0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1492           0 :                     ErrFound = true;
    1493             :                 } else {
    1494             :                     // record use
    1495        1026 :                     FoundZoneMixer(Count1) = true;
    1496        1026 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1497             :                 }
    1498             :             }
    1499             :         }
    1500        1089 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1501             :             // PIU Units
    1502          63 :             if (PoweredInductionUnits::PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
    1503             :         }
    1504        1089 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1505             :             // fourPipeInduction units
    1506           6 :             if (HVACSingleDuctInduc::FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
    1507           4 :                 FoundZoneMixer(Count1) = true;
    1508             :         }
    1509        1089 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1510             :             // Exhaust Systems
    1511           2 :             if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
    1512           2 :                 FoundZoneMixer(Count1) = true;
    1513             :         }
    1514             :     }
    1515         795 :     FoundNames.deallocate();
    1516             : 
    1517         795 :     if (!all(FoundReturnPlenum)) {
    1518           0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1519           0 :             if (FoundReturnPlenum(Count1)) continue;
    1520           0 :             ShowSevereError(state,
    1521           0 :                             format("AirLoopHVAC:ReturnPlenum=\"{}\", not found on any AirLoopHVAC:ReturnPath.",
    1522           0 :                                    state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1523             :         }
    1524             :     }
    1525             : 
    1526         795 :     if (!all(FoundZoneMixer)) {
    1527           0 :         for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1528           0 :             if (FoundZoneMixer(Count1)) continue;
    1529           0 :             ShowSevereError(state,
    1530           0 :                             format("AirLoopHVAC:ZoneMixer=\"{}\", not found on any AirLoopHVAC:ReturnPath, AirLoopHVAC:ExhaustSystem, "
    1531             :                                    "AirTerminal:SingleDuct:SeriesPIU:Reheat,",
    1532           0 :                                    state.dataMixerComponent->MixerCond(Count1).MixerName));
    1533           0 :             ShowContinueError(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat or AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction.");
    1534             :         }
    1535             :     }
    1536             : 
    1537         795 :     FoundReturnPlenum.deallocate();
    1538         795 :     FoundZoneMixer.deallocate();
    1539             : 
    1540         795 :     if (ErrFound) {
    1541           0 :         ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
    1542             :     } else {
    1543         795 :         ShowMessage(state, "All Return Air Paths passed integrity testing");
    1544             :     }
    1545         795 : }
    1546             : 
    1547    37687013 : void CalcComponentSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1548             :                                        Real64 const TDB2,      // dry-bulb temperature at state 2 {C}
    1549             :                                        Real64 const W2,        // humidity ratio at state 2
    1550             :                                        Real64 const TDB1,      // dry-bulb temperature at  at state 1 {C}
    1551             :                                        Real64 const W1,        // humidity ratio at state 1
    1552             :                                        Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1553             :                                        Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1554             :                                        Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1555             : )
    1556             : {
    1557             : 
    1558             :     // Purpose:
    1559             :     // returns total, sensible and latent heat rate of change of moist air transitioning
    1560             :     // between two states. The moist air energy transfer can be cooling or heating process
    1561             :     // across a cooling, a heating coil, or an HVAC component.
    1562             : 
    1563             :     // Methodology:
    1564             :     // Q_total = m_dot * (h2 - h1)
    1565             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1);
    1566             :     // or Q_sensible = m_dot * cp_moistair_MinHumRat * (TDB2 - TDB1)
    1567             :     //    cp_moistair_MinHumRat = Psychrometrics::PsyCpAirFnW(min(W2, W1));
    1568             :     // Q_latent = Q_total - Q_latent;
    1569             : 
    1570    37687013 :     TotalOutput = 0.0;
    1571    37687013 :     LatentOutput = 0.0;
    1572    37687013 :     SensibleOutput = 0.0;
    1573    37687013 :     if (MassFlow > 0.0) {
    1574    36281989 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
    1575    36281989 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
    1576    36281989 :         LatentOutput = TotalOutput - SensibleOutput;                                                // latent addition/removal rate, {W}
    1577             :     }
    1578    37687013 : }
    1579             : 
    1580    61740243 : void CalcZoneSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1581             :                                   Real64 const TDBEquip,  // dry-bulb temperature at equipment outlet {C}
    1582             :                                   Real64 const WEquip,    // humidity ratio at equipment outlet
    1583             :                                   Real64 const TDBZone,   // dry-bulb temperature at zone air node {C}
    1584             :                                   Real64 const WZone,     // humidity ratio at zone air node
    1585             :                                   Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1586             :                                   Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1587             :                                   Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1588             : )
    1589             : {
    1590             : 
    1591             :     // Purpose:
    1592             :     // returns total, sensible and latent heat rate of transfer between the supply air zone inlet
    1593             :     // node and zone air node. The moist air energy transfer can be cooling or heating depending
    1594             :     // on the supply air zone inlet node and zone air node conditions.
    1595             : 
    1596             :     // Methodology:
    1597             :     // Q_total = m_dot * (hEquip - hZone)
    1598             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1599             :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1600             :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1601             :     // Q_latent = Q_total - Q_latent;
    1602             : 
    1603    61740243 :     TotalOutput = 0.0;
    1604    61740243 :     LatentOutput = 0.0;
    1605    61740243 :     SensibleOutput = 0.0;
    1606    61740243 :     if (MassFlow > 0.0) {
    1607    49194861 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
    1608    49194861 :                                   Psychrometrics::PsyHFnTdbW(TDBZone, WZone));                         // total addition/removal rate, {W};
    1609    49194861 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1610    49194861 :         LatentOutput = TotalOutput - SensibleOutput;                                                   // latent addition/removal rate, {W}
    1611             :     }
    1612    61740243 : }
    1613             : 
    1614    51146948 : Real64 calcZoneSensibleOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
    1615             :                               Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
    1616             :                               Real64 const TDBZone,  // dry-bulb temperature at zone air node {C}
    1617             :                               Real64 const WZone     // humidity ratio at zone air node
    1618             : )
    1619             : {
    1620             : 
    1621             :     // Purpose:
    1622             :     // returns sensible heat rate of transfer between the supply air zone inlet node and
    1623             :     // zone air node. The moist air energy transfer can be cooling or heating depending
    1624             :     // on the supply air zone inlet node and zone air node conditions.
    1625             : 
    1626             :     // Methodology:
    1627             :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1628             :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1629             :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1630             : 
    1631    51146948 :     Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
    1632    51146948 :     if (MassFlow > 0.0) {
    1633    41159518 :         sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1634             :     }
    1635    51146948 :     return sensibleOutput;
    1636             : }
    1637             : } // namespace EnergyPlus

Generated by: LCOV version 1.14