LCOV - code coverage report
Current view: top level - EnergyPlus - GeneralRoutines.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 58.2 % 796 463
Test Date: 2025-06-02 12:03:30 Functions: 93.3 % 15 14

            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         7691 : 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         7691 :     int constexpr iReverseAction = 1;
     168         7691 :     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         7691 :     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         7691 :                                                   "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         7691 :     auto &ZoneInterHalf = state.dataGeneralRoutines->ZoneInterHalf;
     194         7691 :     auto &ZoneController = state.dataGeneralRoutines->ZoneController;
     195              : 
     196         7691 :     if (ControlCompTypeNum != 0) {
     197         7685 :         SimCompNum = ControlCompTypeNum;
     198              :     } else {
     199            6 :         SimCompNum = Util::FindItemInSortedList(CompType, ListOfComponents, NumComponents);
     200            6 :         ControlCompTypeNum = SimCompNum;
     201              :     }
     202              : 
     203         7691 :     int Iter = 0; // Iteration limit for the interval halving process
     204         7691 :     bool Converged = false;
     205         7691 :     bool WaterCoilAirFlowControl = false; // True if controlling air flow through water coil, water flow fixed
     206         7691 :     Real64 LoadMet = 0.0;                 // Actual output of unit (watts)
     207         7691 :     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         7691 :     ZoneController.SetPoint = 0.0;
     212              : 
     213              :     // Set to converged controller
     214         7691 :     ZoneInterHalf.MaxFlowCalc = true;
     215         7691 :     ZoneInterHalf.MinFlowCalc = false;
     216         7691 :     ZoneInterHalf.NormFlowCalc = false;
     217         7691 :     ZoneInterHalf.MinFlowResult = false;
     218         7691 :     ZoneInterHalf.MaxResult = 1.0;
     219         7691 :     ZoneInterHalf.MinResult = 0.0;
     220              : 
     221              :     // Start the Solution Iteration
     222        85721 :     while (!Converged) {
     223              : 
     224        84279 :         if (FirstHVACIteration) {
     225        42195 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail = MaxFlow;
     226        42195 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail = MinFlow;
     227              :             // Check to make sure that the Minimum Flow rate is less than the max.
     228        42195 :             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        84279 :         if ((SimCompNum == 3) && (!present(AirMassFlow))) {
     239          504 :             ZoneController.MaxSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail;
     240          504 :             ZoneController.MinSetPoint = state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMinAvail;
     241              :         } else {
     242        83775 :             ZoneController.MaxSetPoint =
     243        83775 :                 min(state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMaxAvail, state.dataLoopNodes->Node(ActuatedNode).MassFlowRateMax);
     244        83775 :             ZoneController.MinSetPoint =
     245        83775 :                 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        84279 :         if (ZoneInterHalf.MaxFlowCalc) {
     249         7691 :             ZoneController.CalculatedSetPoint = ZoneController.MaxSetPoint;
     250         7691 :             ZoneInterHalf.MaxFlow = ZoneController.MaxSetPoint;
     251         7691 :             ZoneInterHalf.MaxFlowCalc = false;
     252         7691 :             ZoneInterHalf.MinFlowCalc = true;
     253              :             // Record the maximum flow rates and set the flow to the minimum and find results
     254        76588 :         } else if (ZoneInterHalf.MinFlowCalc) {
     255         7691 :             ZoneInterHalf.MaxResult = ZoneController.SensedValue;
     256         7691 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     257         7691 :             ZoneInterHalf.MinFlow = ZoneController.MinSetPoint;
     258         7691 :             ZoneInterHalf.MinFlowCalc = false;
     259         7691 :             ZoneInterHalf.MinFlowResult = true;
     260              :             // Record the minimum results and set flow to half way between the max and min and find results
     261        68897 :         } else if (ZoneInterHalf.MinFlowResult) {
     262         7685 :             ZoneInterHalf.MinResult = ZoneController.SensedValue;
     263         7685 :             HalvingPrec = (ZoneInterHalf.MaxResult - ZoneInterHalf.MinResult) * iter_fac;
     264         7685 :             ZoneInterHalf.MidFlow = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     265         7685 :             ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MinFlow) / 2.0;
     266         7685 :             ZoneInterHalf.MinFlowResult = false;
     267         7685 :             ZoneInterHalf.NormFlowCalc = true;
     268              :             // Record the Mid results and check all possibilities and start interval halving procedure
     269        61212 :         } else if (ZoneInterHalf.NormFlowCalc) {
     270        61212 :             ZoneInterHalf.MidResult = ZoneController.SensedValue;
     271              : 
     272              :             // First check to see if the component is running; if not converge and return
     273        61212 :             if (ZoneInterHalf.MaxResult == ZoneInterHalf.MinResult) {
     274              :                 // Set to converged controller
     275          134 :                 ZoneInterHalf.MaxFlowCalc = true;
     276          134 :                 ZoneInterHalf.MinFlowCalc = false;
     277          134 :                 ZoneInterHalf.NormFlowCalc = false;
     278          134 :                 ZoneInterHalf.MinFlowResult = false;
     279          134 :                 ZoneInterHalf.MaxResult = 1.0;
     280          134 :                 ZoneInterHalf.MinResult = 0.0;
     281          134 :                 if ((SimCompNum >= 4) && (SimCompNum <= 6)) { // hot water baseboards use min flow
     282            6 :                     ZoneController.CalculatedSetPoint = 0.0;  // CR7253
     283              :                 } else {
     284          128 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow; // CR7253
     285              :                 }
     286              :                 // Set the Actuated node MassFlowRate with zero value
     287          134 :                 if (plantLoc.loopNum) { // this is a plant component
     288            8 :                     PlantUtilities::SetActuatedBranchFlowRate(state,
     289            8 :                                                               ZoneController.CalculatedSetPoint,
     290              :                                                               ActuatedNode,
     291              :                                                               plantLoc,
     292              :                                                               false); // Autodesk:OPTIONAL LoopSide, BranchIndex used without PRESENT check
     293              :                 } else {                                              // assume not a plant component
     294          126 :                     state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     295              :                 }
     296          134 :                 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        61078 :             if (ZoneInterHalf.MaxResult <= ZoneInterHalf.MinResult) {
     305            0 :                 if (WaterCoilAirFlowControl) {
     306            0 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     307              :                 } else {
     308            0 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     309              :                 }
     310              :                 // set to converged controller
     311            0 :                 Converged = true;
     312            0 :                 ZoneInterHalf.MaxFlowCalc = true;
     313            0 :                 ZoneInterHalf.MinFlowCalc = false;
     314            0 :                 ZoneInterHalf.NormFlowCalc = false;
     315            0 :                 ZoneInterHalf.MinFlowResult = false;
     316            0 :                 ZoneInterHalf.MaxResult = 1.0;
     317            0 :                 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        61078 :                 if (ZoneController.SetPoint <= ZoneInterHalf.MinResult) {
     323            0 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MinFlow;
     324              :                     // Set to Converged Controller
     325            0 :                     Converged = true;
     326            0 :                     ZoneInterHalf.MaxFlowCalc = true;
     327            0 :                     ZoneInterHalf.MinFlowCalc = false;
     328            0 :                     ZoneInterHalf.NormFlowCalc = false;
     329            0 :                     ZoneInterHalf.MinFlowResult = false;
     330            0 :                     ZoneInterHalf.MaxResult = 1.0;
     331            0 :                     ZoneInterHalf.MinResult = 0.0;
     332              :                     // Then check if too hot and if so set it to the maximum flow
     333        61078 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MaxResult) {
     334         1442 :                     ZoneController.CalculatedSetPoint = ZoneInterHalf.MaxFlow;
     335              :                     // Set to Converged Controller
     336         1442 :                     Converged = true;
     337         1442 :                     ZoneInterHalf.MaxFlowCalc = true;
     338         1442 :                     ZoneInterHalf.MinFlowCalc = false;
     339         1442 :                     ZoneInterHalf.NormFlowCalc = false;
     340         1442 :                     ZoneInterHalf.MinFlowResult = false;
     341         1442 :                     ZoneInterHalf.MaxResult = 1.0;
     342         1442 :                     ZoneInterHalf.MinResult = 0.0;
     343              :                     // If between the max and mid set to new flow and raise min to mid
     344        59636 :                 } else if (ZoneController.SetPoint >= ZoneInterHalf.MidResult) {
     345        22490 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MaxFlow + ZoneInterHalf.MidFlow) / 2.0;
     346        22490 :                     ZoneInterHalf.MinFlow = ZoneInterHalf.MidFlow;
     347        22490 :                     ZoneInterHalf.MinResult = ZoneInterHalf.MidResult;
     348        22490 :                     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        37146 :                     ZoneController.CalculatedSetPoint = (ZoneInterHalf.MinFlow + ZoneInterHalf.MidFlow) / 2.0;
     352        37146 :                     ZoneInterHalf.MaxFlow = ZoneInterHalf.MidFlow;
     353        37146 :                     ZoneInterHalf.MaxResult = ZoneInterHalf.MidResult;
     354        37146 :                     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        84145 :         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        84145 :         } else if (ZoneController.CalculatedSetPoint < ZoneController.MinSetPoint) {
     372            0 :             ZoneController.CalculatedSetPoint = ZoneController.MinSetPoint;
     373            0 :             Converged = true;
     374            0 :             ZoneInterHalf.MaxFlowCalc = true;
     375            0 :             ZoneInterHalf.MinFlowCalc = false;
     376            0 :             ZoneInterHalf.NormFlowCalc = false;
     377            0 :             ZoneInterHalf.MinFlowResult = false;
     378            0 :             ZoneInterHalf.MaxResult = 1.0;
     379            0 :             ZoneInterHalf.MinResult = 0.0;
     380              :         }
     381              : 
     382              :         // check if hunting down around the limit of a significant mass flow in systems.
     383        84145 :         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        84145 :         if (plantLoc.loopNum) { // this is a plant component
     396        83767 :             PlantUtilities::SetActuatedBranchFlowRate(state,
     397        83767 :                                                       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          378 :             state.dataLoopNodes->Node(ActuatedNode).MassFlowRate = ZoneController.CalculatedSetPoint;
     403              :         }
     404              : 
     405              :         // The denominator of the control signal should be no less than 100 watts
     406        84145 :         Real64 Denom = sign(max(std::abs(QZnReq), 100.0), QZnReq);
     407        84145 :         if (present(Action)) {
     408            0 :             if (Action == iNormalAction) {
     409            0 :                 Denom = max(std::abs(QZnReq), 100.0);
     410            0 :             } else if (Action == iReverseAction) {
     411            0 :                 Denom = -max(std::abs(QZnReq), 100.0);
     412              :             } else {
     413            0 :                 ShowFatalError(state, format("ControlCompOutput: Illegal Action argument =[{}]", Action));
     414              :             }
     415              :         }
     416              : 
     417        84145 :         switch (SimCompNum) {      // Tuned If block changed to switch
     418            0 :         case ParallelPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:PARALLELPIU:REHEAT'
     419              :             // simulate series piu reheat coil
     420            0 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     421              :             // Calculate the control signal (the variable we are forcing to zero)
     422            0 :             CpAir = Psychrometrics::PsyCpAirFnW(
     423            0 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     424            0 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     425            0 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     426            0 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     427            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     428            0 :             break;
     429              : 
     430            0 :         case SeriesPIUReheatNum: // 'AIRTERMINAL:SINGLEDUCT:SERIESPIU:REHEAT'
     431              :             // simulate series piu reheat coil
     432            0 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     433              :             // Calculate the control signal (the variable we are forcing to zero)
     434            0 :             CpAir = Psychrometrics::PsyCpAirFnW(
     435            0 :                 state.dataLoopNodes->Node(TempOutNode).HumRat); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     436            0 :             LoadMet = CpAir * state.dataLoopNodes->Node(TempOutNode).MassFlowRate *
     437            0 :                       (state.dataLoopNodes->Node(TempOutNode).Temp -
     438            0 :                        state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     439            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     440            0 :             break;
     441              : 
     442         6801 :         case HeatingCoilWaterNum: // 'COIL:HEATING:WATER'
     443              :             // Simulate reheat coil for the VAV system
     444         6801 :             WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, CompNum);
     445              :             // Calculate the control signal (the variable we are forcing to zero)
     446         6801 :             CpAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(TempOutNode).HumRat);
     447         6801 :             if (present(AirMassFlow)) {
     448         6423 :                 LoadMet = AirMassFlow * CpAir * state.dataLoopNodes->Node(TempOutNode).Temp;
     449         6423 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     450              :             } else {
     451          378 :                 WaterCoilAirFlowControl = true;
     452          378 :                 LoadMet = state.dataLoopNodes->Node(TempOutNode).MassFlowRate * CpAir *
     453          378 :                           (state.dataLoopNodes->Node(TempOutNode).Temp -
     454          378 :                            state.dataLoopNodes->Node(TempInNode).Temp); // Autodesk:OPTIONAL TempInNode, TempOutNode used without PRESENT check
     455          378 :                 ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     456              :             }
     457         6801 :             break;
     458              : 
     459        77329 :         case BBWaterConvOnlyNum: // 'ZONEHVAC:BASEBOARD:CONVECTIVE:WATER'
     460              :             // Simulate baseboard
     461        77329 :             BaseboardRadiator::SimHWConvective(state, CompNum, LoadMet);
     462              :             // Calculate the control signal (the variable we are forcing to zero)
     463        77329 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     464        77329 :             break;
     465              : 
     466            0 :         case BBSteamRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:STEAM'
     467              :             // Simulate baseboard
     468            0 :             SteamBaseboardRadiator::CalcSteamBaseboard(state, CompNum, LoadMet);
     469              :             // Calculate the control signal (the variable we are forcing to zero)
     470            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     471            0 :             break;
     472              : 
     473            0 :         case BBWaterRadConvNum: // 'ZONEHVAC:BASEBOARD:RADIANTCONVECTIVE:WATER'
     474              :             // Simulate baseboard
     475            0 :             HWBaseboardRadiator::CalcHWBaseboard(state, CompNum, LoadMet);
     476              :             // Calculate the control signal (the variable we are forcing to zero)
     477            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     478            0 :             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            0 :         case OutdoorAirUnitNum: //'ZONEHVAC:OUTDOORAIRUNIT'
     488              :             // Simulate outdoor air unit components
     489            0 :             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            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     493            0 :             break;
     494              : 
     495           15 :         case UnitHeaterNum: // 'ZONEHVAC:UNITHEATER'
     496              :             // Simulate unit heater components
     497           15 :             UnitHeater::CalcUnitHeaterComponents(state, CompNum, FirstHVACIteration, LoadMet);
     498              :             // Calculate the control signal (the variable we are forcing to zero)
     499           15 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     500           15 :             break;
     501              : 
     502            0 :         case UnitVentilatorNum: // 'ZONEHVAC:UNITVENTILATOR'
     503              :             // Simulate unit ventilator components
     504            0 :             UnitVentilator::CalcUnitVentilatorComponents(state, CompNum, FirstHVACIteration, LoadMet);
     505              :             // Calculate the control signal (the variable we are forcing to zero)
     506            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     507            0 :             break;
     508              : 
     509            0 :         case VentilatedSlabNum: // 'ZONEHVAC:VENTILATEDSLAB'
     510              :             // Simulate unit ventilator components
     511            0 :             VentilatedSlab::CalcVentilatedSlabComps(state, CompNum, FirstHVACIteration, LoadMet);
     512              :             // Calculate the control signal (the variable we are forcing to zero)
     513            0 :             ZoneController.SensedValue = (LoadMet - QZnReq) / Denom;
     514            0 :             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        84145 :         if (std::abs(ZoneController.SensedValue) <= ControlOffset || std::abs(ZoneController.SensedValue) <= HalvingPrec) {
     523              :             // Set to converged controller
     524         6115 :             ZoneInterHalf.MaxFlowCalc = true;
     525         6115 :             ZoneInterHalf.MinFlowCalc = false;
     526         6115 :             ZoneInterHalf.NormFlowCalc = false;
     527         6115 :             ZoneInterHalf.MinFlowResult = false;
     528         6115 :             ZoneInterHalf.MaxResult = 1.0;
     529         6115 :             ZoneInterHalf.MinResult = 0.0;
     530         6115 :             break;
     531              :         }
     532        78030 :         if (!Converged) {
     533        76588 :             bool BBConvergeCheckFlag = BBConvergeCheck(SimCompNum, ZoneInterHalf.MaxFlow, ZoneInterHalf.MinFlow);
     534        76588 :             if (BBConvergeCheckFlag) {
     535              :                 // Set to converged controller
     536            0 :                 ZoneInterHalf.MaxFlowCalc = true;
     537            0 :                 ZoneInterHalf.MinFlowCalc = false;
     538            0 :                 ZoneInterHalf.NormFlowCalc = false;
     539            0 :                 ZoneInterHalf.MinFlowResult = false;
     540            0 :                 ZoneInterHalf.MaxResult = 1.0;
     541            0 :                 ZoneInterHalf.MinResult = 0.0;
     542            0 :                 break;
     543              :             }
     544              :         }
     545              : 
     546        78030 :         ++Iter;
     547        78030 :         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        78030 :         } else if (Iter > MaxIter * 2) {
     576            0 :             break;
     577              :         }
     578              : 
     579              :     } // End of the Convergence Iteration
     580              : }
     581              : 
     582        76594 : 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        76594 :     if (SimCompNum != BBSteamRadConvNum && SimCompNum != BBWaterRadConvNum) {
     606              :         // For all zone equipment except radiant/convective baseboard (steam and water) units:
     607        76590 :         BBConvergeCheck = false;
     608              :     } else {
     609              :         // For steam and water radiant/convective baseboard units:
     610            4 :         if ((MaxFlow - MinFlow) > BBIterLimit) {
     611            2 :             BBConvergeCheck = false;
     612              :         } else {
     613            2 :             BBConvergeCheck = true;
     614              :         }
     615              :     }
     616              : 
     617        76594 :     return BBConvergeCheck;
     618              : }
     619              : 
     620          185 : 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          185 :     if (!state.dataSize->SysSizingRunDone) {
     638            1 :         ShowSevereError(state, format("For autosizing of {} {}, a system sizing run must be done.", CompType, CompName));
     639            1 :         if (state.dataSize->NumSysSizInput == 0) {
     640            0 :             ShowContinueError(state, "No \"Sizing:System\" objects were entered.");
     641              :         }
     642            1 :         if (!state.dataGlobal->DoSystemSizing) {
     643            3 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do System Sizing Calculation" set to Yes.)");
     644              :         }
     645            3 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     646              :     }
     647          184 : }
     648              : 
     649          120 : 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          120 :     AirLoopWasSized = false;
     657          120 :     if (state.dataSize->SysSizingRunDone) {
     658          101 :         for (int ThisAirSysSizineInputLoop = 1; ThisAirSysSizineInputLoop <= state.dataSize->NumSysSizInput; ++ThisAirSysSizineInputLoop) {
     659           96 :             if (state.dataSize->SysSizInput(ThisAirSysSizineInputLoop).AirLoopNum == AirLoopNum) {
     660           94 :                 AirLoopWasSized = true;
     661           94 :                 break;
     662              :             }
     663              :         }
     664              :     }
     665          120 : }
     666              : 
     667          355 : 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          355 :     if (!state.dataSize->ZoneSizingRunDone) {
     685            3 :         ShowSevereError(state, format("For autosizing of {} {}, a zone sizing run must be done.", CompType, CompName));
     686            3 :         if (state.dataSize->NumZoneSizingInput == 0) {
     687            9 :             ShowContinueError(state, "No \"Sizing:Zone\" objects were entered.");
     688              :         }
     689            3 :         if (!state.dataGlobal->DoZoneSizing) {
     690            9 :             ShowContinueError(state, R"(The "SimulationControl" object did not have the field "Do Zone Sizing Calculation" set to Yes.)");
     691              :         }
     692            9 :         ShowFatalError(state, "Program terminates due to previously shown condition(s).");
     693              :     }
     694          352 : }
     695              : 
     696           69 : 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           69 :     ZoneWasSized = false;
     710           69 :     if (state.dataSize->ZoneSizingRunDone) {
     711           58 :         for (int ThisSizingInput = 1; ThisSizingInput <= state.dataSize->NumZoneSizingInput; ++ThisSizingInput) {
     712           23 :             if (state.dataSize->ZoneSizingInput(ThisSizingInput).ZoneNum == ZoneNum) {
     713           20 :                 ZoneWasSized = true;
     714           20 :                 break;
     715              :             }
     716              :         }
     717              :     }
     718           69 : }
     719              : 
     720         1817 : 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         1817 :     IsNotOK = false;
     745              : 
     746         3634 :     int ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, std::string{CompType}, CompName);
     747              : 
     748         1817 :     if (ItemNum < 0) {
     749            1 :         ShowSevereError(state, format("During {} Input, Invalid Component Type input={}", CallString, CompType));
     750            1 :         ShowContinueError(state, format("Component name={}", CompName));
     751            1 :         IsNotOK = true;
     752         1816 :     } else if (ItemNum == 0) {
     753           30 :         ShowSevereError(state, format("During {} Input, Invalid Component Name input={}", CallString, CompName));
     754           30 :         ShowContinueError(state, format("Component type={}", CompType));
     755           30 :         IsNotOK = true;
     756              :     }
     757         1817 : }
     758              : 
     759            0 : 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            0 :     IsNotOK = false;
     785              : 
     786            0 :     int ItemNum = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, CompType, CompValType, CompName);
     787              : 
     788            0 :     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            0 :     } 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            0 : }
     798              : 
     799        73537 : 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        73537 :     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        73537 :     if (sched != nullptr) {
     825            0 :         Real64 BasinHeaterSch = sched->getCurrentVal();
     826            0 :         if (Capacity > 0.0 && BasinHeaterSch > 0.0) {
     827            0 :             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        73537 :         if (Capacity > 0.0) {
     832            2 :             Power = max(0.0, Capacity * (SetPointTemp - state.dataEnvrn->OutDryBulbTemp));
     833              :         }
     834              :     }
     835        73537 : }
     836              : 
     837           75 : 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           75 :     Array2D_int ValRetAPaths;
     850           75 :     Array2D_int NumRAPNodes;
     851           75 :     Array2D_int ValSupAPaths;
     852           75 :     Array2D_int NumSAPNodes;
     853              : 
     854           75 :     NumSAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     855           75 :     NumRAPNodes.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     856           75 :     ValRetAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     857           75 :     ValSupAPaths.allocate(state.dataLoopNodes->NumOfNodes, state.dataHVACGlobal->NumPrimaryAirSys);
     858           75 :     NumSAPNodes = 0;
     859           75 :     NumRAPNodes = 0;
     860           75 :     ValRetAPaths = 0;
     861           75 :     ValSupAPaths = 0;
     862              : 
     863           75 :     TestSupplyAirPathIntegrity(state, errFlag);
     864           75 :     if (errFlag) {
     865            0 :         ErrFound = true;
     866              :     }
     867           75 :     TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
     868           75 :     if (errFlag) {
     869            0 :         ErrFound = true;
     870              :     }
     871              : 
     872              :     // Final tests, look for duplicate nodes
     873           99 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     874           24 :         if (ValRetAPaths(1, Loop) != 0) {
     875           24 :             continue;
     876              :         }
     877            0 :         if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) {
     878            0 :             continue;
     879              :         }
     880            0 :         ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
     881              :     }
     882              : 
     883           99 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     884          760 :         for (int Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
     885          736 :             int TestNode = ValRetAPaths(Loop1, Loop);
     886          736 :             int Count = 0;
     887         1472 :             for (int Loop2 = 1; Loop2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop2) {
     888         2943 :                 for (int Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
     889         2943 :                     if (Loop2 == Loop && Loop1 == Loop3) {
     890           85 :                         continue; // Don't count test node
     891              :                     }
     892         2858 :                     if (ValRetAPaths(Loop3, Loop2) == 0) {
     893          736 :                         break;
     894              :                     }
     895         2122 :                     if (ValRetAPaths(Loop3, Loop2) == TestNode) {
     896            0 :                         ++Count;
     897              :                     }
     898              :                 }
     899              :             }
     900          736 :             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           75 :     NumSAPNodes.deallocate();
     910           75 :     NumRAPNodes.deallocate();
     911           75 :     ValRetAPaths.deallocate();
     912           75 :     ValSupAPaths.deallocate();
     913           75 : }
     914              : 
     915           75 : 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           75 :     std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
     928           75 :     Array1D_bool FoundSupplyPlenum;
     929           75 :     Array1D_bool FoundZoneSplitter;
     930           75 :     Array1D_string FoundNames;
     931           75 :     int NumErr = 0; // Error Counter //Autodesk:Init Initialization added
     932              : 
     933              :     // Do by Paths
     934          150 :     ShowMessage(state, "Testing Individual Supply Air Path Integrity");
     935           75 :     ErrFound = false;
     936              : 
     937           75 :     print(state.files.bnd, "{}\n", "! ===============================================================");
     938              :     static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
     939           75 :     print(state.files.bnd, "{}\n", Format_700);
     940           75 :     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           75 :     print(state.files.bnd, "{}\n", Format_702);
     943              :     static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
     944           75 :     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           75 :     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           75 :     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           75 :     print(state.files.bnd, "{}\n", Format_708);
     954              : 
     955           99 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
     956              : 
     957              :         // Determine which air loop this supply air path is connected to
     958           24 :         int Found = 0;
     959           24 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
     960           24 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
     961           24 :             Found = 0;
     962           48 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
     963           24 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
     964           24 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2)) {
     965           24 :                     Found = Count2;
     966              :                 }
     967              :             }
     968           24 :             if (Found != 0) {
     969           24 :                 break;
     970              :             }
     971              :         }
     972           24 :         if (Found == 0) {
     973            0 :             PrimaryAirLoopName = "**Unknown**";
     974              :         }
     975              : 
     976           24 :         print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
     977           24 :         print(state.files.bnd, "   #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
     978              : 
     979           24 :         std::string AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
     980              : 
     981           48 :         for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
     982              : 
     983           24 :             print(state.files.bnd,
     984              :                   "   Supply Air Path Component,{},{},{},{}\n",
     985              :                   Count,
     986           24 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     987           24 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
     988              :                   PrimaryAirLoopName);
     989              : 
     990              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
     991           24 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
     992              : 
     993           24 :             switch (CompType) {
     994            0 :             case AirLoopHVACCompType::SupplyPlenum: {
     995            0 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
     996            0 :                     if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
     997            0 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count)) {
     998            0 :                         continue;
     999              :                     }
    1000            0 :                     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            0 :                     print(state.files.bnd,
    1012              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1013            0 :                           state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
    1014            0 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
    1015            0 :                         print(state.files.bnd,
    1016              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1017              :                               Count1,
    1018            0 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1019            0 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1020            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
    1021            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
    1022              :                               PrimaryAirLoopName);
    1023              :                     }
    1024              :                 }
    1025            0 :             } break;
    1026           24 :             case AirLoopHVACCompType::ZoneSplitter: {
    1027           48 :                 for (int Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
    1028           24 :                     if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
    1029           24 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count)) {
    1030            0 :                         continue;
    1031              :                     }
    1032           24 :                     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           24 :                     print(state.files.bnd,
    1044              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1045           24 :                           state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
    1046           61 :                     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
    1047           37 :                         print(state.files.bnd,
    1048              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1049              :                               Count1,
    1050           37 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1051           37 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1052           37 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
    1053           37 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
    1054              :                               PrimaryAirLoopName);
    1055              :                     }
    1056              :                 }
    1057           24 :             } 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           24 :         if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
    1068              :             static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
    1069           24 :             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           24 :             print(state.files.bnd, "{}\n", Format_706);
    1072           24 :             print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
    1073           85 :             for (int Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
    1074           61 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
    1075           24 :                     print(state.files.bnd,
    1076              :                           "   Supply Air Path Node,Inlet Node,{},{},{}\n",
    1077              :                           Count2,
    1078           24 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1079              :                           PrimaryAirLoopName);
    1080           37 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
    1081            0 :                     print(state.files.bnd,
    1082              :                           "   Supply Air Path Node,Through Node,{},{},{}\n",
    1083              :                           Count2,
    1084            0 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1085              :                           PrimaryAirLoopName);
    1086           37 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
    1087           37 :                     print(state.files.bnd,
    1088              :                           "   Supply Air Path Node,Outlet Node,{},{},{}\n",
    1089              :                           Count2,
    1090           37 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1091              :                           PrimaryAirLoopName);
    1092              :                 }
    1093              :             }
    1094              :         }
    1095           24 :     }
    1096              : 
    1097           75 :     if (state.dataSplitterComponent->NumSplitters == 0) {
    1098           51 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
    1099            0 :             SplitterComponent::GetSplitterInput(state);
    1100              :         }
    1101              :     }
    1102           75 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1103           75 :         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           75 :     FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
    1110           75 :     FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
    1111           75 :     FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
    1112           75 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1113            0 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1114            0 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1115            0 :                 if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1116            0 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM") {
    1117            0 :                     continue;
    1118              :                 }
    1119            0 :                 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            0 :                     FoundSupplyPlenum(Count1) = true;
    1128            0 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1129              :                 }
    1130              :             }
    1131              :         }
    1132              :     }
    1133           75 :     FoundNames.deallocate();
    1134           75 :     FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
    1135           99 :     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1136           48 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1137           48 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1138           24 :                 if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
    1139           72 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1140           24 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER") {
    1141            0 :                     continue;
    1142              :                 }
    1143           24 :                 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           24 :                     FoundZoneSplitter(Count1) = true;
    1152           24 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1153              :                 }
    1154              :             }
    1155              :         }
    1156              :     }
    1157           75 :     FoundNames.deallocate();
    1158              : 
    1159           75 :     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           75 :     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           75 :     FoundSupplyPlenum.deallocate();
    1182           75 :     FoundZoneSplitter.deallocate();
    1183              : 
    1184           75 :     if (ErrFound) {
    1185            0 :         ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
    1186              :     } else {
    1187          225 :         ShowMessage(state, "All Supply Air Paths passed integrity testing");
    1188              :     }
    1189           75 : }
    1190              : 
    1191           75 : 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           75 :     std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
    1227           75 :     Array1D_bool FoundReturnPlenum;
    1228           75 :     Array1D_bool FoundZoneMixer;
    1229           75 :     Array1D_string FoundNames;
    1230           75 :     Array1D_int AllNodes;
    1231              : 
    1232              :     // Formats
    1233              : 
    1234              :     // Do by Paths
    1235          150 :     ShowMessage(state, "Testing Individual Return Air Path Integrity");
    1236           75 :     ErrFound = false;
    1237           75 :     int NumErr = 0;
    1238              : 
    1239           75 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1240              :     static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
    1241           75 :     print(state.files.bnd, "{}\n", Format_700);
    1242           75 :     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           75 :     print(state.files.bnd, "{}\n", Format_702);
    1245              :     static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
    1246           75 :     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           75 :     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           75 :     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           75 :     print(state.files.bnd, "{}\n", Format_708);
    1256              : 
    1257           75 :     AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
    1258              : 
    1259           99 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1260              :         //             Determine which air loop this supply air path is connected to
    1261           24 :         int Found = 0;
    1262           24 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
    1263           24 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1264           24 :             Found = 0;
    1265           48 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
    1266           24 :                 if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
    1267           24 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2)) {
    1268           24 :                     Found = Count2;
    1269              :                 }
    1270              :             }
    1271           24 :             if (Found != 0) {
    1272           24 :                 break;
    1273              :             }
    1274              :         }
    1275           24 :         if (Found == 0) {
    1276            0 :             PrimaryAirLoopName = "**Unknown**";
    1277              :         }
    1278              : 
    1279           24 :         print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
    1280              : 
    1281           24 :         int NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
    1282           24 :         print(state.files.bnd, "   #Components on Return Air Path,{}\n", NumComp);
    1283              : 
    1284           24 :         std::string const &AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
    1285              : 
    1286           24 :         int MixerCount = 0;
    1287           48 :         for (int Count = 1; Count <= NumComp; ++Count) {
    1288           24 :             print(state.files.bnd,
    1289              :                   "   Return Air Path Component,{},{},{},{}\n",
    1290              :                   Count,
    1291           24 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
    1292           24 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
    1293              :                   PrimaryAirLoopName);
    1294              : 
    1295           24 :             if (Util::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
    1296           24 :                 ++MixerCount;
    1297              :             }
    1298              :         }
    1299              : 
    1300           24 :         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           24 :         AllNodes = 0;
    1308           24 :         int CountNodes = 0;
    1309              : 
    1310           24 :         if (NumComp > 0) {
    1311              : 
    1312              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1313           24 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
    1314              : 
    1315           24 :             switch (CompType) {
    1316           24 :             case AirLoopHVACCompType::ZoneMixer: {
    1317           54 :                 for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1318           30 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName) {
    1319            6 :                         continue;
    1320              :                     }
    1321              :                     // Found correct Mixer (by name), check outlet node vs. return air path outlet node
    1322           24 :                     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           24 :                         ++CountNodes;
    1333           24 :                         AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
    1334           24 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataMixerComponent->MixerCond(Count2).OutletNode) {
    1335           24 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1336              :                         }
    1337           61 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1338           37 :                             ++CountNodes;
    1339           37 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1340              :                         }
    1341              :                     }
    1342           24 :                     print(state.files.bnd,
    1343              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1344           24 :                           state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
    1345           61 :                     for (int Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
    1346           37 :                         print(state.files.bnd,
    1347              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1348              :                               Count1,
    1349           37 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1350           37 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1351           37 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
    1352           37 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
    1353              :                               PrimaryAirLoopName);
    1354              :                     }
    1355              :                 }
    1356           24 :             } break;
    1357            0 :             case AirLoopHVACCompType::ReturnPlenum: {
    1358            0 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1359            0 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
    1360            0 :                         state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName) {
    1361            0 :                         continue;
    1362              :                     }
    1363            0 :                     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            0 :                         ++CountNodes;
    1375            0 :                         AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
    1376            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode) {
    1377            0 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1378              :                         }
    1379            0 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1380            0 :                             ++CountNodes;
    1381            0 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1382              :                         }
    1383              :                     }
    1384            0 :                     print(state.files.bnd,
    1385              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1386            0 :                           state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
    1387            0 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
    1388            0 :                         print(state.files.bnd,
    1389              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1390              :                               Count1,
    1391            0 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1392            0 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1393            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
    1394            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
    1395              :                               PrimaryAirLoopName);
    1396              :                     }
    1397              :                 }
    1398            0 :             } break;
    1399            0 :             default: // This already validated in GetReturnAirPath
    1400            0 :                 break;
    1401              :             }
    1402              :         }
    1403              : 
    1404           24 :         if (NumComp > 1) {
    1405            0 :             for (int Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
    1406              : 
    1407              :                 AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1408            0 :                     getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
    1409              : 
    1410            0 :                 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            0 :                 case AirLoopHVACCompType::ReturnPlenum: {
    1424            0 :                     for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1425            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1426            0 :                             state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName) {
    1427            0 :                             continue;
    1428              :                         }
    1429            0 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1430            0 :                             ++CountNodes;
    1431            0 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1432              :                         }
    1433              :                     }
    1434            0 :                 } break;
    1435            0 :                 default: // This already validated in GetReturnAirPath
    1436            0 :                     break;
    1437              :                 }
    1438              :             }
    1439              :         }
    1440           24 :         if (CountNodes > 0) {
    1441              :             static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
    1442           24 :             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           24 :             print(state.files.bnd, "{}\n", Format_706);
    1445           24 :             print(state.files.bnd, "   #Nodes on Return Air Path,{}\n", CountNodes);
    1446           85 :             for (int Count2 = 1; Count2 <= CountNodes; ++Count2) {
    1447           61 :                 if (Count2 == 1) {
    1448           24 :                     print(state.files.bnd,
    1449              :                           "   Return Air Path Node,Outlet Node,{},{},{}\n",
    1450              :                           Count2,
    1451           24 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1452              :                           PrimaryAirLoopName);
    1453              :                 } else {
    1454           37 :                     print(state.files.bnd,
    1455              :                           "   Return Air Path Node,Inlet Node,{},{},{}\n",
    1456              :                           Count2,
    1457           37 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1458              :                           PrimaryAirLoopName);
    1459              :                 }
    1460              :             }
    1461              :         }
    1462              :         // Determine Air Loop this Return Air Path is on
    1463           24 :         for (int Count2 = 1; Count2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count2) {
    1464           24 :             if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
    1465           24 :                 if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
    1466           24 :                     const int WAirLoop = Count2;
    1467           24 :                     ValRetAPaths(_, WAirLoop) = 0;
    1468           24 :                     ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
    1469           24 :                     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           75 :     AllNodes.deallocate();
    1480              : 
    1481           75 :     if (state.dataMixerComponent->NumMixers == 0) {
    1482           51 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
    1483            0 :             MixerComponent::GetMixerInput(state);
    1484              :         }
    1485              :     }
    1486           75 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1487           75 :         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           75 :     FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
    1494           75 :     FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
    1495           75 :     FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
    1496           75 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1497            0 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1498            0 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1499            0 :                 if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1500            0 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM") {
    1501            0 :                     continue;
    1502              :                 }
    1503            0 :                 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            0 :                     FoundReturnPlenum(Count1) = true;
    1512            0 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1513              :                 }
    1514              :             }
    1515              :         }
    1516            0 :         if (PurchasedAirManager::CheckPurchasedAirForReturnPlenum(state, Count1)) {
    1517            0 :             FoundReturnPlenum(Count1) = true;
    1518              :         }
    1519              :     }
    1520           75 :     FoundNames.deallocate();
    1521           75 :     FoundNames.allocate(state.dataMixerComponent->NumMixers);
    1522          105 :     for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1523           60 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1524           60 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1525           54 :                 if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1526           24 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER") {
    1527            6 :                     continue;
    1528              :                 }
    1529           24 :                 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           24 :                     FoundZoneMixer(Count1) = true;
    1537           24 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1538              :                 }
    1539              :             }
    1540              :         }
    1541           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1542              :             // PIU Units
    1543            6 :             if (PoweredInductionUnits::PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1544            6 :                 FoundZoneMixer(Count1) = true;
    1545              :             }
    1546              :         }
    1547           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1548              :             // fourPipeInduction units
    1549            0 :             if (HVACSingleDuctInduc::FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1550            0 :                 FoundZoneMixer(Count1) = true;
    1551              :             }
    1552              :         }
    1553           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1554              :             // Exhaust Systems
    1555            0 :             if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) {
    1556            0 :                 FoundZoneMixer(Count1) = true;
    1557              :             }
    1558              :         }
    1559              :     }
    1560           75 :     FoundNames.deallocate();
    1561              : 
    1562           75 :     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           75 :     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           75 :     FoundReturnPlenum.deallocate();
    1587           75 :     FoundZoneMixer.deallocate();
    1588              : 
    1589           75 :     if (ErrFound) {
    1590            0 :         ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
    1591              :     } else {
    1592          225 :         ShowMessage(state, "All Return Air Paths passed integrity testing");
    1593              :     }
    1594           75 : }
    1595              : 
    1596       296531 : 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       296531 :     TotalOutput = 0.0;
    1620       296531 :     LatentOutput = 0.0;
    1621       296531 :     SensibleOutput = 0.0;
    1622       296531 :     if (MassFlow > 0.0) {
    1623       276105 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
    1624       276105 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
    1625       276105 :         LatentOutput = TotalOutput - SensibleOutput;                                                // latent addition/removal rate, {W}
    1626              :     }
    1627       296531 : }
    1628              : 
    1629       195790 : 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       195790 :     TotalOutput = 0.0;
    1653       195790 :     LatentOutput = 0.0;
    1654       195790 :     SensibleOutput = 0.0;
    1655       195790 :     if (MassFlow > 0.0) {
    1656       141050 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
    1657       141050 :                                   Psychrometrics::PsyHFnTdbW(TDBZone, WZone));                         // total addition/removal rate, {W};
    1658       141050 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1659       141050 :         LatentOutput = TotalOutput - SensibleOutput;                                                   // latent addition/removal rate, {W}
    1660              :     }
    1661       195790 : }
    1662              : 
    1663       401829 : 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       401829 :     Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
    1681       401829 :     if (MassFlow > 0.0) {
    1682       322958 :         sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1683              :     }
    1684       401829 :     return sensibleOutput;
    1685              : }
    1686              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1