LCOV - code coverage report
Current view: top level - EnergyPlus - GeneralRoutines.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 76.3 % 796 607
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 15 15

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : #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     12136644 : 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     12136644 :     int constexpr iReverseAction = 1;
     168     12136644 :     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     12136644 :     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     12136644 :                                                   "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     12136644 :     auto &ZoneInterHalf = state.dataGeneralRoutines->ZoneInterHalf;
     194     12136644 :     auto &ZoneController = state.dataGeneralRoutines->ZoneController;
     195              : 
     196     12136644 :     if (ControlCompTypeNum != 0) {
     197     12134396 :         SimCompNum = ControlCompTypeNum;
     198              :     } else {
     199         2248 :         SimCompNum = Util::FindItemInSortedList(CompType, ListOfComponents, NumComponents);
     200         2248 :         ControlCompTypeNum = SimCompNum;
     201              :     }
     202              : 
     203     12136644 :     int Iter = 0; // Iteration limit for the interval halving process
     204     12136644 :     bool Converged = false;
     205     12136644 :     bool WaterCoilAirFlowControl = false; // True if controlling air flow through water coil, water flow fixed
     206     12136644 :     Real64 LoadMet = 0.0;                 // Actual output of unit (watts)
     207     12136644 :     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     12136644 :     ZoneController.SetPoint = 0.0;
     212              : 
     213              :     // Set to converged controller
     214     12136644 :     ZoneInterHalf.MaxFlowCalc = true;
     215     12136644 :     ZoneInterHalf.MinFlowCalc = false;
     216     12136644 :     ZoneInterHalf.NormFlowCalc = false;
     217     12136644 :     ZoneInterHalf.MinFlowResult = false;
     218     12136644 :     ZoneInterHalf.MaxResult = 1.0;
     219     12136644 :     ZoneInterHalf.MinResult = 0.0;
     220              : 
     221              :     // Start the Solution Iteration
     222     88842451 :     while (!Converged) {
     223              : 
     224     84342952 :         if (FirstHVACIteration) {
     225     34825681 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail = MaxFlow;
     226     34825681 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail = MinFlow;
     227              :             // Check to make sure that the Minimum Flow rate is less than the max.
     228     34825681 :             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     84342952 :         if ((SimCompNum == 3) && (!present(AirMassFlow))) {
     239      6909146 :             ZoneController.MaxSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail;
     240      6909146 :             ZoneController.MinSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail;
     241              :         } else {
     242     77433806 :             ZoneController.MaxSetPoint =
     243     77433806 :                 min(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMax);
     244     77433806 :             ZoneController.MinSetPoint =
     245     77433806 :                 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     84342952 :         if (ZoneInterHalf.MaxFlowCalc) {
     249     12136644 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     250     12136644 :             ZoneInterHalf.MaxFlow = ZoneController.MaxSetPoint;
     251     12136644 :             ZoneInterHalf.MaxFlowCalc = false;
     252     12136644 :             ZoneInterHalf.MinFlowCalc = true;
     253              :             // Record the maximum flow rates and set the flow to the minimum and find results
     254     72206308 :         } else if (ZoneInterHalf.MinFlowCalc) {
     255     12030012 :             ZoneInterHalf.MaxResult = ZoneController.SensedValue;
     256     12030012 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     257     12030012 :             ZoneInterHalf.MinFlow = ZoneController.MinSetPoint;
     258     12030012 :             ZoneInterHalf.MinFlowCalc = false;
     259     12030012 :             ZoneInterHalf.MinFlowResult = true;
     260              :             // Record the minimum results and set flow to half way between the max and min and find results
     261     60176296 :         } else if (ZoneInterHalf.MinFlowResult) {
     262     11977175 :             ZoneInterHalf.MinResult = ZoneController.SensedValue;
     263     11977175 :             HalvingPrec = (ZoneInterHalf.MaxResult - ZoneInterHalf.MinResult) * iter_fac;
     264     11977175 :             ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     265     11977175 :             ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     266     11977175 :             ZoneInterHalf.MinFlowResult = false;
     267     11977175 :             ZoneInterHalf.NormFlowCalc = true;
     268              :             // Record the Mid results and check all possibilities and start interval halving procedure
     269     48199121 :         } else if (ZoneInterHalf.NormFlowCalc) {
     270     48199121 :             ZoneInterHalf.MidResult = ZoneController.SensedValue;
     271              : 
     272              :             // First check to see if the component is running; if not converge and return
     273     48199121 :             if (ZoneInterHalf.MaxResult == ZoneInterHalf.MinResult) {
     274              :                 // Set to converged controller
     275      1359586 :                 ZoneInterHalf.MaxFlowCalc = true;
     276      1359586 :                 ZoneInterHalf.MinFlowCalc = false;
     277      1359586 :                 ZoneInterHalf.NormFlowCalc = false;
     278      1359586 :                 ZoneInterHalf.MinFlowResult = false;
     279      1359586 :                 ZoneInterHalf.MaxResult = 1.0;
     280      1359586 :                 ZoneInterHalf.MinResult = 0.0;
     281      1359586 :                 if ((SimCompNum >= 4) && (SimCompNum <= 6)) { // hot water baseboards use min flow
     282        18719 :                     ZoneController.CalculatedSetPoint = 0.0;  // CR7253
     283              :                 } else {
     284      1340867 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow; // CR7253
     285              :                 }
     286              :                 // Set the Actuated node MassFlowRate with zero value
     287      1359586 :                 if (plantLoc.loopNum) { // this is a plant component
     288       527263 :                     PlantUtilities::SetActuatedBranchFlowRate(state,
     289       527263 :                                                               ZoneController.CalculatedSetPoint,
     290              :                                                               ActuatedNode,
     291              :                                                               plantLoc,
     292              :                                                               false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     293              :                 } else {                                              // assume not a plant component
     294       832323 :                     state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     295              :                 }
     296      1359586 :                 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     46839535 :             if (ZoneInterHalf.MaxResult <= ZoneInterHalf.MinResult) {
     305       161080 :                 if (WaterCoilAirFlowControl) {
     306       161037 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     307              :                 } else {
     308           43 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     309              :                 }
     310              :                 // set to converged controller
     311       161080 :                 Converged = true;
     312       161080 :                 ZoneInterHalf.MaxFlowCalc = true;
     313       161080 :                 ZoneInterHalf.MinFlowCalc = false;
     314       161080 :                 ZoneInterHalf.NormFlowCalc = false;
     315       161080 :                 ZoneInterHalf.MinFlowResult = false;
     316       161080 :                 ZoneInterHalf.MaxResult = 1.0;
     317       161080 :                 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     46678455 :                 if (ZoneController.SetPoint <= ZoneInterHalf.MinResult) {
     323         7841 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     324              :                     // Set to Converged Controller
     325         7841 :                     Converged = true;
     326         7841 :                     ZoneInterHalf.MaxFlowCalc = true;
     327         7841 :                     ZoneInterHalf.MinFlowCalc = false;
     328         7841 :                     ZoneInterHalf.NormFlowCalc = false;
     329         7841 :                     ZoneInterHalf.MinFlowResult = false;
     330         7841 :                     ZoneInterHalf.MaxResult = 1.0;
     331         7841 :                     ZoneInterHalf.MinResult = 0.0;
     332              :                     // Then check if too hot and if so set it to the maximum flow
     333     46670614 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MaxResult) {
     334      4330561 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     335              :                     // Set to Converged Controller
     336      4330561 :                     Converged = true;
     337      4330561 :                     ZoneInterHalf.MaxFlowCalc = true;
     338      4330561 :                     ZoneInterHalf.MinFlowCalc = false;
     339      4330561 :                     ZoneInterHalf.NormFlowCalc = false;
     340      4330561 :                     ZoneInterHalf.MinFlowResult = false;
     341      4330561 :                     ZoneInterHalf.MaxResult = 1.0;
     342      4330561 :                     ZoneInterHalf.MinResult = 0.0;
     343              :                     // If between the max and mid set to new flow and raise min to mid
     344     42340053 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MidResult) {
     345     18233689 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     346     18233689 :                     ZoneInterHalf.MinFlow = ZoneInterHalf.MidFlow;
     347     18233689 :                     ZoneInterHalf.MinResult = ZoneInterHalf.MidResult;
     348     18233689 :                     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     24106364 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     352     24106364 :                     ZoneInterHalf.MaxFlow = ZoneInterHalf.MidFlow;
     353     24106364 :                     ZoneInterHalf.MaxResult = ZoneInterHalf.MidResult;
     354     24106364 :                     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     82983366 :         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     82983366 :         } 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     82983366 :         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     82983366 :         if (plantLoc.loopNum) { // this is a plant component
     396     76906543 :             PlantUtilities::SetActuatedBranchFlowRate(state,
     397     76906543 :                                                       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      6076823 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     403              :         }
     404              : 
     405              :         // The denominator of the control signal should be no less than 100 watts
     406     82983366 :         Real64 Denom = sign(max(std::abs(QZnReq), 100.0), QZnReq);
     407     82983366 :         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     82983366 :         switch (SimCompNum) {      // Tuned If block changed to switch
     418       158624 :         case ParallelPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT'
     419              :             // simulate series piu reheat coil
     420       158624 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     421              :             // Calculate the control signal (the variable we are forcing to zero)
     422       158624 :             CpAir = Psychrometrics::PsyCpAirFnW(
     423       158624 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     424       158624 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     425       158624 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     426       158624 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     427       158624 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     428       158624 :             break;
     429              : 
     430       956174 :         case SeriesPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT'
     431              :             // simulate series piu reheat coil
     432       956174 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     433              :             // Calculate the control signal (the variable we are forcing to zero)
     434       956174 :             CpAir = Psychrometrics::PsyCpAirFnW(
     435       956174 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     436       956174 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     437       956174 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     438       956174 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     439       956174 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     440       956174 :             break;
     441              : 
     442     79263522 :         case HeatingCoilWaterNum: // 'COIL:HEATING:WATER'
     443              :             // Simulate reheat coil for the VAV system
     444     79263522 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     445              :             // Calculate the control signal (the variable we are forcing to zero)
     446     79263522 :             CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat);
     447     79263522 :             if (present(AirMassFlow)) {
     448     73186699 :                 LoadMet = AirMassFlow * CpAir * state.dataLoopNodes->Node(TempOutNode).Temp;
     449     73186699 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     450              :             } else {
     451      6076823 :                 WaterCoilAirFlowControl = true;
     452      6076823 :                 LoadMet = state.dataLoopNodes->Node(TempOutNode).MassFlowRate * CpAir *
     453      6076823 :                           (state.dataLoopNodes->Node(TempOutNode).Temp -
     454      6076823 :                            state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     455      6076823 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     456              :             }
     457     79263522 :             break;
     458              : 
     459       731260 :         case BBWaterConvOnlyNum: // 'ZONEHVAC:BASEBOARD:CONVECTIVE:WATER'
     460              :             // Simulate baseboard
     461       731260 :             BaseboardRadiator::SimHWConvective(state, CompNum, LoadMet);
     462              :             // Calculate the control signal (the variable we are forcing to zero)
     463       731260 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     464       731260 :             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       159997 :         case BBWaterRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER'
     474              :             // Simulate baseboard
     475       159997 :             HWBaseboardRadiator::CalcHWBaseboard(state, CompNum, LoadMet);
     476              :             // Calculate the control signal (the variable we are forcing to zero)
     477       159997 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     478       159997 :             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       427228 :         case VentilatedSlabNum: // 'ZONEHVAC:VENTILATEDSLAB'
     510              :             // Simulate unit ventilator components
     511       427228 :             VentilatedSlab::CalcVentilatedSlabComps(state, CompNum, FirstHVACIteration, LoadMet);
     512              :             // Calculate the control signal (the variable we are forcing to zero)
     513       427228 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     514       427228 :             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     82983366 :         if (std::abs(ZoneController.SensedValue) <= ControlOffset || std::abs(ZoneController.SensedValue) <= HalvingPrec) {
     523              :             // Set to converged controller
     524      6211546 :             ZoneInterHalf.MaxFlowCalc = true;
     525      6211546 :             ZoneInterHalf.MinFlowCalc = false;
     526      6211546 :             ZoneInterHalf.NormFlowCalc = false;
     527      6211546 :             ZoneInterHalf.MinFlowResult = false;
     528      6211546 :             ZoneInterHalf.MaxResult = 1.0;
     529      6211546 :             ZoneInterHalf.MinResult = 0.0;
     530      6211546 :             break;
     531              :         }
     532     76771820 :         if (!Converged) {
     533     72272321 :             bool BBConvergeCheckFlag = BBConvergeCheck(SimCompNum, ZoneInterHalf.MaxFlow, ZoneInterHalf.MinFlow);
     534     72272321 :             if (BBConvergeCheckFlag) {
     535              :                 // Set to converged controller
     536        66013 :                 ZoneInterHalf.MaxFlowCalc = true;
     537        66013 :                 ZoneInterHalf.MinFlowCalc = false;
     538        66013 :                 ZoneInterHalf.NormFlowCalc = false;
     539        66013 :                 ZoneInterHalf.MinFlowResult = false;
     540        66013 :                 ZoneInterHalf.MaxResult = 1.0;
     541        66013 :                 ZoneInterHalf.MinResult = 0.0;
     542        66013 :                 break;
     543              :             }
     544              :         }
     545              : 
     546     76705807 :         ++Iter;
     547     76705807 :         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     76705807 :         } else if (Iter > MaxIter * 2) {
     576            0 :             break;
     577              :         }
     578              : 
     579              :     } // End of the Convergence Iteration
     580              : }
     581              : 
     582     72272321 : 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     72272321 :     if (SimCompNum != BBSteamRadConvNum && SimCompNum != BBWaterRadConvNum) {
     606              :         // For all zone equipment except radiant/convective baseboard (steam and water) units:
     607     72092720 :         BBConvergeCheck = false;
     608              :     } else {
     609              :         // For steam and water radiant/convective baseboard units:
     610       179601 :         if ((MaxFlow - MinFlow) > BBIterLimit) {
     611       113588 :             BBConvergeCheck = false;
     612              :         } else {
     613        66013 :             BBConvergeCheck = true;
     614              :         }
     615              :     }
     616              : 
     617     72272321 :     return BBConvergeCheck;
     618              : }
     619              : 
     620         6962 : 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         6962 :     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         6962 : }
     648              : 
     649         5212 : 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         5212 :     AirLoopWasSized = false;
     657         5212 :     if (state.dataSize->SysSizingRunDone) {
     658        25889 :         for (int ThisAirSysSizineInputLoop = 1; ThisAirSysSizineInputLoop <= state.dataSize->NumSysSizInput; ++ThisAirSysSizineInputLoop) {
     659        25881 :             if (state.dataSize->SysSizInput(ThisAirSysSizineInputLoop).AirLoopNum == AirLoopNum) {
     660         4851 :                 AirLoopWasSized = true;
     661         4851 :                 break;
     662              :             }
     663              :         }
     664              :     }
     665         5212 : }
     666              : 
     667        15155 : 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        15155 :     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        15155 : }
     695              : 
     696         1032 : 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         1032 :     ZoneWasSized = false;
     710         1032 :     if (state.dataSize->ZoneSizingRunDone) {
     711        33618 :         for (int ThisSizingInput = 1; ThisSizingInput <= state.dataSize->NumZoneSizingInput; ++ThisSizingInput) {
     712        33618 :             if (state.dataSize->ZoneSizingInput(ThisSizingInput).ZoneNum == ZoneNum) {
     713         1032 :                 ZoneWasSized = true;
     714         1032 :                 break;
     715              :             }
     716              :         }
     717              :     }
     718         1032 : }
     719              : 
     720        38093 : 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        38093 :     IsNotOK = false;
     745              : 
     746        76186 :     int ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, std::string{CompType}, CompName);
     747              : 
     748        38093 :     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        38093 :     } 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        38093 : }
     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      4881177 : void CalcBasinHeaterPower(EnergyPlusData &state,
     800              :                           Real64 const Capacity,     // Basin heater capacity per degree C below setpoint (W/C)
     801              :                           Sched::Schedule *sched,    // 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      4881177 :     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      4881177 :     if (sched != nullptr) {
     825        39418 :         Real64 BasinHeaterSch = sched->getCurrentVal();
     826        39418 :         if (Capacity > 0.0 && BasinHeaterSch > 0.0) {
     827        39412 :             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      4841759 :         if (Capacity > 0.0) {
     832       176638 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
     833              :         }
     834              :     }
     835      4881177 : }
     836              : 
     837          800 : 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          800 :     Array2D_int ValRetAPaths;
     850          800 :     Array2D_int NumRAPNodes;
     851          800 :     Array2D_int ValSupAPaths;
     852          800 :     Array2D_int NumSAPNodes;
     853              : 
     854          800 :     NumSAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     855          800 :     NumRAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     856          800 :     ValRetAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     857          800 :     ValSupAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     858          800 :     NumSAPNodes = 0;
     859          800 :     NumRAPNodes = 0;
     860          800 :     ValRetAPaths = 0;
     861          800 :     ValSupAPaths = 0;
     862              : 
     863          800 :     TestSupplyAirPathIntegrity(state, errFlag);
     864          800 :     if (errFlag) {
     865            0 :         ErrFound = true;
     866              :     }
     867          800 :     TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
     868          800 :     if (errFlag) {
     869            0 :         ErrFound = true;
     870              :     }
     871              : 
     872              :     // Final tests, look for duplicate nodes
     873         2021 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     874         1221 :         if (ValRetAPaths(1, Loop) != 0) {
     875         1208 :             continue;
     876              :         }
     877           13 :         if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) {
     878            0 :             continue;
     879              :         }
     880           13 :         ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
     881              :     }
     882              : 
     883         2021 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     884       379523 :         for (int Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
     885       378302 :             int TestNode = ValRetAPaths(Loop1, Loop);
     886       378302 :             int Count = 0;
     887     15427964 :             for (int Loop2 = 1; Loop2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop2) {
     888     51301813 :                 for (int Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
     889     51301813 :                     if (Loop2 == Loop && Loop1 == Loop3) {
     890         6326 :                         continue; // Don't count test node
     891              :                     }
     892     51295487 :                     if (ValRetAPaths(Loop3, Loop2) == 0) {
     893     15049662 :                         break;
     894              :                     }
     895     36245825 :                     if (ValRetAPaths(Loop3, Loop2) == TestNode) {
     896            0 :                         ++Count;
     897              :                     }
     898              :                 }
     899              :             }
     900       378302 :             if (Count > 0) {
     901            0 :                 ShowSevereError(state, "Duplicate Node detected in Return Air Paths");
     902            0 :                 ShowContinueError(state, format("Test Node={}", state.dataLoopNodes->NodeID(TestNode)));
     903            0 :                 ShowContinueError(state, format("In Air Path={}", state.dataAirLoop->AirToZoneNodeInfo(Loop).AirLoopName));
     904            0 :                 ErrFound = true;
     905              :             }
     906              :         }
     907              :     }
     908              : 
     909          800 :     NumSAPNodes.deallocate();
     910          800 :     NumRAPNodes.deallocate();
     911          800 :     ValRetAPaths.deallocate();
     912          800 :     ValSupAPaths.deallocate();
     913          800 : }
     914              : 
     915          800 : void TestSupplyAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
     916              : {
     917              : 
     918              :     // SUBROUTINE INFORMATION:
     919              :     //       AUTHOR         Linda Lawrie
     920              :     //       DATE WRITTEN   March 2003
     921              : 
     922              :     // PURPOSE OF THIS SUBROUTINE:
     923              :     // This subroutine tests supply air path integrity and displays the loop for each branch.
     924              :     // Also, input and output nodes.
     925              : 
     926              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     927          800 :     std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
     928          800 :     Array1D_bool FoundSupplyPlenum;
     929          800 :     Array1D_bool FoundZoneSplitter;
     930          800 :     Array1D_string FoundNames;
     931          800 :     int NumErr = 0; // Error Counter //Autodesk:Init Initialization added
     932              : 
     933              :     // Do by Paths
     934         1600 :     ShowMessage(state, "Testing Individual Supply Air Path Integrity");
     935          800 :     ErrFound = false;
     936              : 
     937          800 :     print(state.files.bnd, "{}\n", "! ===============================================================");
     938              :     static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
     939          800 :     print(state.files.bnd, "{}\n", Format_700);
     940          800 :     print(state.files.bnd, " #Supply Air Paths,{}\n", state.dataZoneEquip->NumSupplyAirPaths);
     941              :     static constexpr std::string_view Format_702("! <Supply Air Path>,<Supply Air Path Count>,<Supply Air Path Name>,<AirLoopHVAC Name>");
     942          800 :     print(state.files.bnd, "{}\n", Format_702);
     943              :     static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
     944          800 :     print(state.files.bnd, "{}\n", Format_703);
     945              :     static constexpr std::string_view Format_704(
     946              :         "! <Supply Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
     947          800 :     print(state.files.bnd, "{}\n", Format_704);
     948              :     static constexpr std::string_view Format_707("! <#Outlet Nodes on Supply Air Path Component>,<Number of Nodes>");
     949          800 :     print(state.files.bnd, "{}\n", Format_707);
     950              :     static constexpr std::string_view Format_708(
     951              :         "! <Supply Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
     952              :         "Node Name>,<AirLoopHVAC Name>");
     953          800 :     print(state.files.bnd, "{}\n", Format_708);
     954              : 
     955         2025 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
     956              : 
     957              :         // Determine which air loop this supply air path is connected to
     958         1225 :         int Found = 0;
     959         6581 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
     960         6581 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
     961         6581 :             Found = 0;
     962        13178 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
     963         6597 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
     964         6597 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2)) {
     965         1225 :                     Found = Count2;
     966              :                 }
     967              :             }
     968         6581 :             if (Found != 0) {
     969         1225 :                 break;
     970              :             }
     971              :         }
     972         1225 :         if (Found == 0) {
     973            0 :             PrimaryAirLoopName = "**Unknown**";
     974              :         }
     975              : 
     976         1225 :         print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
     977         1225 :         print(state.files.bnd, "   #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
     978              : 
     979         1225 :         std::string AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
     980              : 
     981         2457 :         for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
     982              : 
     983         1232 :             print(state.files.bnd,
     984              :                   "   Supply Air Path Component,{},{},{},{}\n",
     985              :                   Count,
     986         1232 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     987         1232 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
     988              :                   PrimaryAirLoopName);
     989              : 
     990              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
     991         1232 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
     992              : 
     993         1232 :             switch (CompType) {
     994            8 :             case AirLoopHVACCompType::SupplyPlenum: {
     995           20 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
     996           12 :                     if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
     997           12 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count)) {
     998            4 :                         continue;
     999              :                     }
    1000            8 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode)) {
    1001            0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
    1002            0 :                         ShowContinueError(state,
    1003            0 :                                           format("For AirLoopHVAC:SupplyPlenum={}", state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName));
    1004            0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
    1005            0 :                         ShowContinueError(state,
    1006            0 :                                           format("Encountered node name (supply plenum)={}",
    1007            0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(1))));
    1008            0 :                         ErrFound = true;
    1009            0 :                         ++NumErr;
    1010              :                     }
    1011            8 :                     print(state.files.bnd,
    1012              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1013            8 :                           state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
    1014           20 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
    1015           12 :                         print(state.files.bnd,
    1016              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1017              :                               Count1,
    1018           12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1019           12 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1020           12 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
    1021           12 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
    1022              :                               PrimaryAirLoopName);
    1023              :                     }
    1024              :                 }
    1025            8 :             } break;
    1026         1224 :             case AirLoopHVACCompType::ZoneSplitter: {
    1027        13176 :                 for (int Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
    1028        11952 :                     if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
    1029        11952 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count)) {
    1030        10728 :                         continue;
    1031              :                     }
    1032         1224 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)) {
    1033            0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
    1034            0 :                         ShowContinueError(state,
    1035            0 :                                           format("For AirLoopHVAC:ZoneSplitter={}", state.dataSplitterComponent->SplitterCond(Count2).SplitterName));
    1036            0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
    1037            0 :                         ShowContinueError(state,
    1038            0 :                                           format("Encountered node name (zone splitter)={}",
    1039            0 :                                                  state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)));
    1040            0 :                         ErrFound = true;
    1041            0 :                         ++NumErr;
    1042              :                     }
    1043         1224 :                     print(state.files.bnd,
    1044              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1045         1224 :                           state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
    1046         5045 :                     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
    1047         3821 :                         print(state.files.bnd,
    1048              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1049              :                               Count1,
    1050         3821 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1051         3821 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1052         3821 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
    1053         3821 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
    1054              :                               PrimaryAirLoopName);
    1055              :                     }
    1056              :                 }
    1057         1224 :             } break;
    1058            0 :             default: {
    1059            0 :                 ShowSevereError(
    1060            0 :                     state, format("Invalid Component Type in Supply Air Path={}", state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count)));
    1061            0 :                 ErrFound = true;
    1062            0 :                 ++NumErr;
    1063            0 :             } break;
    1064              :             }
    1065              :         }
    1066              : 
    1067         1225 :         if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
    1068              :             static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
    1069         1225 :             print(state.files.bnd, "{}\n", Format_705);
    1070              :             static constexpr std::string_view Format_706("! <Supply Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1071         1225 :             print(state.files.bnd, "{}\n", Format_706);
    1072         1225 :             print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
    1073         6283 :             for (int Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
    1074         5058 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
    1075         1225 :                     print(state.files.bnd,
    1076              :                           "   Supply Air Path Node,Inlet Node,{},{},{}\n",
    1077              :                           Count2,
    1078         1225 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1079              :                           PrimaryAirLoopName);
    1080         3833 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
    1081            7 :                     print(state.files.bnd,
    1082              :                           "   Supply Air Path Node,Through Node,{},{},{}\n",
    1083              :                           Count2,
    1084            7 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1085              :                           PrimaryAirLoopName);
    1086         3826 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
    1087         3826 :                     print(state.files.bnd,
    1088              :                           "   Supply Air Path Node,Outlet Node,{},{},{}\n",
    1089              :                           Count2,
    1090         3826 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1091              :                           PrimaryAirLoopName);
    1092              :                 }
    1093              :             }
    1094              :         }
    1095         1225 :     }
    1096              : 
    1097          800 :     if (state.dataSplitterComponent->NumSplitters == 0) {
    1098          256 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
    1099            0 :             SplitterComponent::GetSplitterInput(state);
    1100              :         }
    1101              :     }
    1102          800 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1103          605 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum") > 0) {
    1104            0 :             ZonePlenum::GetZonePlenumInput(state);
    1105              :         }
    1106              :     }
    1107              : 
    1108              :     // now the reverse.  is every zone splitter and supply plenum on supply air path
    1109          800 :     FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
    1110          800 :     FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
    1111          800 :     FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
    1112          808 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1113           20 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1114           35 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1115           31 :                 if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1116            8 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM") {
    1117           15 :                     continue;
    1118              :                 }
    1119            8 :                 if (FoundSupplyPlenum(Count1)) {
    1120            0 :                     ShowSevereError(
    1121              :                         state,
    1122            0 :                         format("AirLoopHVAC:SupplyPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1123            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1124            0 :                     ErrFound = true;
    1125              :                 } else {
    1126              :                     // record use
    1127            8 :                     FoundSupplyPlenum(Count1) = true;
    1128            8 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1129              :                 }
    1130              :             }
    1131              :         }
    1132              :     }
    1133          800 :     FoundNames.deallocate();
    1134          800 :     FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
    1135         2024 :     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1136        13176 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1137        23915 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1138        11963 :                 if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
    1139        25150 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1140         1224 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER") {
    1141        10739 :                     continue;
    1142              :                 }
    1143         1224 :                 if (FoundZoneSplitter(Count1)) {
    1144            0 :                     ShowSevereError(
    1145              :                         state,
    1146            0 :                         format("AirLoopHVAC:ZoneSplitter=\"{}\", duplicate entry.", state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1147            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1148            0 :                     ErrFound = true;
    1149              :                 } else {
    1150              :                     // record use
    1151         1224 :                     FoundZoneSplitter(Count1) = true;
    1152         1224 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1153              :                 }
    1154              :             }
    1155              :         }
    1156              :     }
    1157          800 :     FoundNames.deallocate();
    1158              : 
    1159          800 :     if (!all(FoundSupplyPlenum)) {
    1160            0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1161            0 :             if (FoundSupplyPlenum(Count1)) {
    1162            0 :                 continue;
    1163              :             }
    1164            0 :             ShowSevereError(state,
    1165            0 :                             format("AirLoopHVAC:SupplyPlenum=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1166            0 :                                    state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1167              :         }
    1168              :     }
    1169              : 
    1170          800 :     if (!all(FoundZoneSplitter)) {
    1171            0 :         for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1172            0 :             if (FoundZoneSplitter(Count1)) {
    1173            0 :                 continue;
    1174              :             }
    1175            0 :             ShowSevereError(state,
    1176            0 :                             format("AirLoopHVAC:ZoneSplitter=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1177            0 :                                    state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1178              :         }
    1179              :     }
    1180              : 
    1181          800 :     FoundSupplyPlenum.deallocate();
    1182          800 :     FoundZoneSplitter.deallocate();
    1183              : 
    1184          800 :     if (ErrFound) {
    1185            0 :         ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
    1186              :     } else {
    1187         2400 :         ShowMessage(state, "All Supply Air Paths passed integrity testing");
    1188              :     }
    1189          800 : }
    1190              : 
    1191          800 : void TestReturnAirPathIntegrity(EnergyPlusData &state, bool &ErrFound, Array2S_int ValRetAPaths)
    1192              : {
    1193              : 
    1194              :     // SUBROUTINE INFORMATION:
    1195              :     //       AUTHOR         Linda Lawrie
    1196              :     //       DATE WRITTEN   March 2003
    1197              : 
    1198              :     // PURPOSE OF THIS SUBROUTINE:
    1199              :     // This subroutine tests return air path integrity and displays the loop for each branch.
    1200              :     // Also, input and output nodes.
    1201              : 
    1202              :     // REFERENCES:
    1203              :     // Return Air Path Validity Rules:
    1204              :     //  Last component (zone mixer or zone return plenum) must resolve to
    1205              :     //  be the outlet node for the return air path.  Inlets to this component must be outlets from
    1206              :     //  previous components or "controlled zone outlets"?.
    1207              :     //  (though converse not true -- each outlet in previous components do not
    1208              :     //  have to be inlets on this item -- though they must be inputs somewhere in the stream).
    1209              :     //  If multiple components and no mixer, then a zone return plenums "outlet" must
    1210              :     //  be represented as an inlet on a later plenum.  i.e. some zone return plenums are
    1211              :     //  really acting as "mixers" in a sense.  These do not need to be stepwise in succession.
    1212              :     //  Same caveat for inlets from previous item.
    1213              :     //  If multiple components and mixer, then prior condition (nested plenums) is allowed as long as
    1214              :     //  those aren't duplicated as mixer inlets.  (i.e. zone rp 1 => zone rp 2 => zone mixer but
    1215              :     //  zone rp 1 outlet should not also be inlet to mixer.
    1216              :     //  Can have (nzrp -- nested zone return plenum, pzrp -- parallel zone return plenum):
    1217              :     //  nzrp 1 => nzrp 2 & pzrp 3 => zm (inlets from nzrp 2 and pzrp 3).  Or, likewise:
    1218              :     //  pzrp 1 & pzrp 2 => zm => pzrp 3 (outlets from pzrp 1/2 are inlets to zm whose outlet is an
    1219              :     //  inlet to pzrp 3 whose outlet is the outlet for the return air path.
    1220              : 
    1221              :     //  Cannot have duplicate nodes in the "inlet" stream?  (i.e. cannot have same zone feeding two independent
    1222              :     //  plenums, for example).  Similarly, Same return plenum can't be in two air loops nor as two independent
    1223              :     //  return plenums in one return air path.
    1224              : 
    1225              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1226          800 :     std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
    1227          800 :     Array1D_bool FoundReturnPlenum;
    1228          800 :     Array1D_bool FoundZoneMixer;
    1229          800 :     Array1D_string FoundNames;
    1230          800 :     Array1D_int AllNodes;
    1231              : 
    1232              :     // Formats
    1233              : 
    1234              :     // Do by Paths
    1235         1600 :     ShowMessage(state, "Testing Individual Return Air Path Integrity");
    1236          800 :     ErrFound = false;
    1237          800 :     int NumErr = 0;
    1238              : 
    1239          800 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1240              :     static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
    1241          800 :     print(state.files.bnd, "{}\n", Format_700);
    1242          800 :     print(state.files.bnd, " #Return Air Paths,{}\n", state.dataZoneEquip->NumReturnAirPaths);
    1243              :     static constexpr std::string_view Format_702("! <Return Air Path>,<Return Air Path Count>,<Return Air Path Name>,<AirLoopHVAC Name>");
    1244          800 :     print(state.files.bnd, "{}\n", Format_702);
    1245              :     static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
    1246          800 :     print(state.files.bnd, "{}\n", Format_703);
    1247              :     static constexpr std::string_view Format_704(
    1248              :         "! <Return Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
    1249          800 :     print(state.files.bnd, "{}\n", Format_704);
    1250              :     static constexpr std::string_view Format_707("! <#Inlet Nodes on Return Air Path Component>,<Number of Nodes>");
    1251          800 :     print(state.files.bnd, "{}\n", Format_707);
    1252              :     static constexpr std::string_view Format_708(
    1253              :         "! <Return Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
    1254              :         "Node Name>,<AirLoopHVAC Name>");
    1255          800 :     print(state.files.bnd, "{}\n", Format_708);
    1256              : 
    1257          800 :     AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
    1258              : 
    1259         2008 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1260              :         //             Determine which air loop this supply air path is connected to
    1261         1208 :         int Found = 0;
    1262         6550 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
    1263         6550 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1264         6550 :             Found = 0;
    1265        13100 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
    1266         6550 :                 if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
    1267         6550 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2)) {
    1268         1208 :                     Found = Count2;
    1269              :                 }
    1270              :             }
    1271         6550 :             if (Found != 0) {
    1272         1208 :                 break;
    1273              :             }
    1274              :         }
    1275         1208 :         if (Found == 0) {
    1276            0 :             PrimaryAirLoopName = "**Unknown**";
    1277              :         }
    1278              : 
    1279         1208 :         print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
    1280              : 
    1281         1208 :         int NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
    1282         1208 :         print(state.files.bnd, "   #Components on Return Air Path,{}\n", NumComp);
    1283              : 
    1284         1208 :         std::string const &AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
    1285              : 
    1286         1208 :         int MixerCount = 0;
    1287         2532 :         for (int Count = 1; Count <= NumComp; ++Count) {
    1288         1324 :             print(state.files.bnd,
    1289              :                   "   Return Air Path Component,{},{},{},{}\n",
    1290              :                   Count,
    1291         1324 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
    1292         1324 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
    1293              :                   PrimaryAirLoopName);
    1294              : 
    1295         1324 :             if (Util::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
    1296         1040 :                 ++MixerCount;
    1297              :             }
    1298              :         }
    1299              : 
    1300         1208 :         if (MixerCount > 1) {
    1301            0 :             ShowSevereError(state, format("Too many zone mixers in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1302            0 :             ErrFound = true;
    1303            0 :             ++NumErr;
    1304            0 :             continue;
    1305              :         }
    1306              : 
    1307         1208 :         AllNodes = 0;
    1308         1208 :         int CountNodes = 0;
    1309              : 
    1310         1208 :         if (NumComp > 0) {
    1311              : 
    1312              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1313         1208 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
    1314              : 
    1315         1208 :             switch (CompType) {
    1316         1040 :             case AirLoopHVACCompType::ZoneMixer: {
    1317        12598 :                 for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1318        11558 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName) {
    1319        10518 :                         continue;
    1320              :                     }
    1321              :                     // Found correct Mixer (by name), check outlet node vs. return air path outlet node
    1322         1040 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)) {
    1323            0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1324            0 :                         ShowContinueError(state, format("For Connector:Mixer={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1325            0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1326            0 :                         ShowContinueError(state,
    1327            0 :                                           format("Encountered node name (mixer)={}",
    1328            0 :                                                  state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)));
    1329            0 :                         ErrFound = true;
    1330            0 :                         ++NumErr;
    1331              :                     } else {
    1332         1040 :                         ++CountNodes;
    1333         1040 :                         AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
    1334         1040 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataMixerComponent->MixerCond(Count2).OutletNode) {
    1335         1040 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1336              :                         }
    1337         3617 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1338         2577 :                             ++CountNodes;
    1339         2577 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1340              :                         }
    1341              :                     }
    1342         1040 :                     print(state.files.bnd,
    1343              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1344         1040 :                           state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
    1345         3617 :                     for (int Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
    1346         2577 :                         print(state.files.bnd,
    1347              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1348              :                               Count1,
    1349         2577 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1350         2577 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1351         2577 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
    1352         2577 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
    1353              :                               PrimaryAirLoopName);
    1354              :                     }
    1355              :                 }
    1356         1040 :             } break;
    1357          168 :             case AirLoopHVACCompType::ReturnPlenum: {
    1358          518 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1359          350 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
    1360          350 :                         state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName) {
    1361          182 :                         continue;
    1362              :                     }
    1363          168 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)) {
    1364            0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1365            0 :                         ShowContinueError(
    1366            0 :                             state, format("For AirLoopHVAC:ReturnPlenum={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1367            0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1368            0 :                         ShowContinueError(state,
    1369            0 :                                           format("Encountered node name (zone return plenum)={}",
    1370            0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)));
    1371            0 :                         ErrFound = true;
    1372            0 :                         ++NumErr;
    1373              :                     } else {
    1374          168 :                         ++CountNodes;
    1375          168 :                         AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
    1376          168 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode) {
    1377          168 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1378              :                         }
    1379          918 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1380          750 :                             ++CountNodes;
    1381          750 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1382              :                         }
    1383              :                     }
    1384          168 :                     print(state.files.bnd,
    1385              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1386          168 :                           state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
    1387          918 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
    1388          750 :                         print(state.files.bnd,
    1389              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1390              :                               Count1,
    1391          750 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1392          750 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1393          750 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
    1394          750 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
    1395              :                               PrimaryAirLoopName);
    1396              :                     }
    1397              :                 }
    1398          168 :             } break;
    1399            0 :             default: // This already validated in GetReturnAirPath
    1400            0 :                 break;
    1401              :             }
    1402              :         }
    1403              : 
    1404         1208 :         if (NumComp > 1) {
    1405          231 :             for (int Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
    1406              : 
    1407              :                 AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1408          116 :                     getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
    1409              : 
    1410          116 :                 switch (CompType) {
    1411            0 :                 case AirLoopHVACCompType::ZoneMixer: {
    1412            0 :                     for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1413            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1414            0 :                             state.dataMixerComponent->MixerCond(Count2).MixerName) {
    1415            0 :                             continue;
    1416              :                         }
    1417            0 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1418            0 :                             ++CountNodes;
    1419            0 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1420              :                         }
    1421              :                     }
    1422            0 :                 } break;
    1423          116 :                 case AirLoopHVACCompType::ReturnPlenum: {
    1424          456 :                     for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1425          340 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1426          340 :                             state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName) {
    1427          224 :                             continue;
    1428              :                         }
    1429          677 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1430          561 :                             ++CountNodes;
    1431          561 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1432              :                         }
    1433              :                     }
    1434          116 :                 } break;
    1435            0 :                 default: // This already validated in GetReturnAirPath
    1436            0 :                     break;
    1437              :                 }
    1438              :             }
    1439              :         }
    1440         1208 :         if (CountNodes > 0) {
    1441              :             static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
    1442         1208 :             print(state.files.bnd, "{}\n", Format_705);
    1443              :             static constexpr std::string_view Format_706("! <Return Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1444         1208 :             print(state.files.bnd, "{}\n", Format_706);
    1445         1208 :             print(state.files.bnd, "   #Nodes on Return Air Path,{}\n", CountNodes);
    1446         6304 :             for (int Count2 = 1; Count2 <= CountNodes; ++Count2) {
    1447         5096 :                 if (Count2 == 1) {
    1448         1208 :                     print(state.files.bnd,
    1449              :                           "   Return Air Path Node,Outlet Node,{},{},{}\n",
    1450              :                           Count2,
    1451         1208 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1452              :                           PrimaryAirLoopName);
    1453              :                 } else {
    1454         3888 :                     print(state.files.bnd,
    1455              :                           "   Return Air Path Node,Inlet Node,{},{},{}\n",
    1456              :                           Count2,
    1457         3888 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1458              :                           PrimaryAirLoopName);
    1459              :                 }
    1460              :             }
    1461              :         }
    1462              :         // Determine Air Loop this Return Air Path is on
    1463         6550 :         for (int Count2 = 1; Count2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count2) {
    1464         6550 :             if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
    1465         6550 :                 if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
    1466         1208 :                     const int WAirLoop = Count2;
    1467         1208 :                     ValRetAPaths(_, WAirLoop) = 0;
    1468         1208 :                     ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
    1469         1208 :                     break;
    1470              :                 }
    1471              :             } else {
    1472            0 :                 ShowWarningError(state,
    1473            0 :                                  format("TestReturnAirPathIntegrity: Air Loop has no Zone Equipment Return Node={}",
    1474            0 :                                         state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName));
    1475              :             }
    1476              :         }
    1477              :     }
    1478              : 
    1479          800 :     AllNodes.deallocate();
    1480              : 
    1481          800 :     if (state.dataMixerComponent->NumMixers == 0) {
    1482          389 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
    1483            0 :             MixerComponent::GetMixerInput(state);
    1484              :         }
    1485              :     }
    1486          800 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1487          605 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum") > 0) {
    1488            0 :             ZonePlenum::GetZonePlenumInput(state);
    1489              :         }
    1490              :     }
    1491              : 
    1492              :     // now the reverse.  is every zone Mixer and Return plenum on Return air path
    1493          800 :     FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
    1494          800 :     FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
    1495          800 :     FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
    1496         1085 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1497         1104 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1498         1978 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1499         1443 :                 if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1500          284 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM") {
    1501          875 :                     continue;
    1502              :                 }
    1503          284 :                 if (FoundReturnPlenum(Count1)) {
    1504            0 :                     ShowSevereError(
    1505              :                         state,
    1506            0 :                         format("AirLoopHVAC:ReturnPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1507            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1508            0 :                     ErrFound = true;
    1509              :                 } else {
    1510              :                     // record use
    1511          284 :                     FoundReturnPlenum(Count1) = true;
    1512          284 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1513              :                 }
    1514              :             }
    1515              :         }
    1516          285 :         if (PurchasedAirManager::CheckPurchasedAirForReturnPlenum(state, Count1)) {
    1517            1 :             FoundReturnPlenum(Count1) = true;
    1518              :         }
    1519              :     }
    1520          800 :     FoundNames.deallocate();
    1521          800 :     FoundNames.allocate(state.dataMixerComponent->NumMixers);
    1522         1903 :     for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1523        12708 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1524        23719 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1525        13154 :                 if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1526         1040 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER") {
    1527        11074 :                     continue;
    1528              :                 }
    1529         1040 :                 if (FoundZoneMixer(Count1)) {
    1530            0 :                     ShowSevereError(state,
    1531            0 :                                     format("AirLoopHVAC:ZoneMixer=\"{}\", duplicate entry.", state.dataMixerComponent->MixerCond(Count1).MixerName));
    1532            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1533            0 :                     ErrFound = true;
    1534              :                 } else {
    1535              :                     // record use
    1536         1040 :                     FoundZoneMixer(Count1) = true;
    1537         1040 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1538              :                 }
    1539              :             }
    1540              :         }
    1541         1103 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1542              :             // PIU Units
    1543           63 :             if (PoweredInductionUnits::PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1544           57 :                 FoundZoneMixer(Count1) = true;
    1545              :             }
    1546              :         }
    1547         1103 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1548              :             // fourPipeInduction units
    1549            6 :             if (HVACSingleDuctInduc::FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1550            4 :                 FoundZoneMixer(Count1) = true;
    1551              :             }
    1552              :         }
    1553         1103 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1554              :             // Exhaust Systems
    1555            2 :             if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1556            2 :                 FoundZoneMixer(Count1) = true;
    1557              :             }
    1558              :         }
    1559              :     }
    1560          800 :     FoundNames.deallocate();
    1561              : 
    1562          800 :     if (!all(FoundReturnPlenum)) {
    1563            0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1564            0 :             if (FoundReturnPlenum(Count1)) {
    1565            0 :                 continue;
    1566              :             }
    1567            0 :             ShowSevereError(state,
    1568            0 :                             format("AirLoopHVAC:ReturnPlenum=\"{}\", not found on any AirLoopHVAC:ReturnPath.",
    1569            0 :                                    state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1570              :         }
    1571              :     }
    1572              : 
    1573          800 :     if (!all(FoundZoneMixer)) {
    1574            0 :         for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1575            0 :             if (FoundZoneMixer(Count1)) {
    1576            0 :                 continue;
    1577              :             }
    1578            0 :             ShowSevereError(state,
    1579            0 :                             format("AirLoopHVAC:ZoneMixer=\"{}\", not found on any AirLoopHVAC:ReturnPath, AirLoopHVAC:ExhaustSystem, "
    1580              :                                    "AirTerminal:SingleDuct:SeriesPIU:Reheat,",
    1581            0 :                                    state.dataMixerComponent->MixerCond(Count1).MixerName));
    1582            0 :             ShowContinueError(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat or AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction.");
    1583              :         }
    1584              :     }
    1585              : 
    1586          800 :     FoundReturnPlenum.deallocate();
    1587          800 :     FoundZoneMixer.deallocate();
    1588              : 
    1589          800 :     if (ErrFound) {
    1590            0 :         ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
    1591              :     } else {
    1592         2400 :         ShowMessage(state, "All Return Air Paths passed integrity testing");
    1593              :     }
    1594          800 : }
    1595              : 
    1596     37753440 : void CalcComponentSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1597              :                                        Real64 const TDB2,      // dry-bulb temperature at state 2 {C}
    1598              :                                        Real64 const W2,        // humidity ratio at state 2
    1599              :                                        Real64 const TDB1,      // dry-bulb temperature at  at state 1 {C}
    1600              :                                        Real64 const W1,        // humidity ratio at state 1
    1601              :                                        Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1602              :                                        Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1603              :                                        Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1604              : )
    1605              : {
    1606              : 
    1607              :     // Purpose:
    1608              :     // returns total, sensible and latent heat rate of change of moist air transitioning
    1609              :     // between two states. The moist air energy transfer can be cooling or heating process
    1610              :     // across a cooling, a heating coil, or an HVAC component.
    1611              : 
    1612              :     // Methodology:
    1613              :     // Q_total = m_dot * (h2 - h1)
    1614              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1);
    1615              :     // or Q_sensible = m_dot * cp_moistair_MinHumRat * (TDB2 - TDB1)
    1616              :     //    cp_moistair_MinHumRat = Psychrometrics::PsyCpAirFnW(min(W2, W1));
    1617              :     // Q_latent = Q_total - Q_latent;
    1618              : 
    1619     37753440 :     TotalOutput = 0.0;
    1620     37753440 :     LatentOutput = 0.0;
    1621     37753440 :     SensibleOutput = 0.0;
    1622     37753440 :     if (MassFlow > 0.0) {
    1623     36342106 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
    1624     36342106 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
    1625     36342106 :         LatentOutput = TotalOutput - SensibleOutput;                                                // latent addition/removal rate, {W}
    1626              :     }
    1627     37753440 : }
    1628              : 
    1629     75654879 : void CalcZoneSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1630              :                                   Real64 const TDBEquip,  // dry-bulb temperature at equipment outlet {C}
    1631              :                                   Real64 const WEquip,    // humidity ratio at equipment outlet
    1632              :                                   Real64 const TDBZone,   // dry-bulb temperature at zone air node {C}
    1633              :                                   Real64 const WZone,     // humidity ratio at zone air node
    1634              :                                   Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1635              :                                   Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1636              :                                   Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1637              : )
    1638              : {
    1639              : 
    1640              :     // Purpose:
    1641              :     // returns total, sensible and latent heat rate of transfer between the supply air zone inlet
    1642              :     // node and zone air node. The moist air energy transfer can be cooling or heating depending
    1643              :     // on the supply air zone inlet node and zone air node conditions.
    1644              : 
    1645              :     // Methodology:
    1646              :     // Q_total = m_dot * (hEquip - hZone)
    1647              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1648              :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1649              :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1650              :     // Q_latent = Q_total - Q_latent;
    1651              : 
    1652     75654879 :     TotalOutput = 0.0;
    1653     75654879 :     LatentOutput = 0.0;
    1654     75654879 :     SensibleOutput = 0.0;
    1655     75654879 :     if (MassFlow > 0.0) {
    1656     61402707 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
    1657     61402707 :                                   Psychrometrics::PsyHFnTdbW(TDBZone, WZone));                         // total addition/removal rate, {W};
    1658     61402707 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1659     61402707 :         LatentOutput = TotalOutput - SensibleOutput;                                                   // latent addition/removal rate, {W}
    1660              :     }
    1661     75654879 : }
    1662              : 
    1663     54914772 : Real64 calcZoneSensibleOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
    1664              :                               Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
    1665              :                               Real64 const TDBZone,  // dry-bulb temperature at zone air node {C}
    1666              :                               Real64 const WZone     // humidity ratio at zone air node
    1667              : )
    1668              : {
    1669              : 
    1670              :     // Purpose:
    1671              :     // returns sensible heat rate of transfer between the supply air zone inlet node and
    1672              :     // zone air node. The moist air energy transfer can be cooling or heating depending
    1673              :     // on the supply air zone inlet node and zone air node conditions.
    1674              : 
    1675              :     // Methodology:
    1676              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1677              :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1678              :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1679              : 
    1680     54914772 :     Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
    1681     54914772 :     if (MassFlow > 0.0) {
    1682     44465158 :         sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1683              :     }
    1684     54914772 :     return sensibleOutput;
    1685              : }
    1686              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1