LCOV - code coverage report
Current view: top level - EnergyPlus - GeneralRoutines.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 58.7 % 778 457
Test Date: 2025-05-22 16:09:37 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) ErrFound = true;
     865           75 :     TestReturnAirPathIntegrity(state, errFlag, ValRetAPaths);
     866           75 :     if (errFlag) ErrFound = true;
     867              : 
     868              :     // Final tests, look for duplicate nodes
     869           99 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     870           24 :         if (ValRetAPaths(1, Loop) != 0) continue;
     871            0 :         if (state.dataAirLoop->AirToZoneNodeInfo(Loop).NumReturnNodes <= 0) continue;
     872            0 :         ValRetAPaths(1, Loop) = state.dataAirLoop->AirToZoneNodeInfo(Loop).ZoneEquipReturnNodeNum(1);
     873              :     }
     874              : 
     875           99 :     for (int Loop = 1; Loop <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop) {
     876          760 :         for (int Loop1 = 1; Loop1 <= state.dataLoopNodes->NumOfNodes; ++Loop1) {
     877          736 :             int TestNode = ValRetAPaths(Loop1, Loop);
     878          736 :             int Count = 0;
     879         1472 :             for (int Loop2 = 1; Loop2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Loop2) {
     880         2943 :                 for (int Loop3 = 1; Loop3 <= state.dataLoopNodes->NumOfNodes; ++Loop3) {
     881         2943 :                     if (Loop2 == Loop && Loop1 == Loop3) continue; // Don't count test node
     882         2858 :                     if (ValRetAPaths(Loop3, Loop2) == 0) break;
     883         2122 :                     if (ValRetAPaths(Loop3, Loop2) == TestNode) ++Count;
     884              :                 }
     885              :             }
     886          736 :             if (Count > 0) {
     887            0 :                 ShowSevereError(state, "Duplicate Node detected in Return Air Paths");
     888            0 :                 ShowContinueError(state, format("Test Node={}", state.dataLoopNodes->NodeID(TestNode)));
     889            0 :                 ShowContinueError(state, format("In Air Path={}", state.dataAirLoop->AirToZoneNodeInfo(Loop).AirLoopName));
     890            0 :                 ErrFound = true;
     891              :             }
     892              :         }
     893              :     }
     894              : 
     895           75 :     NumSAPNodes.deallocate();
     896           75 :     NumRAPNodes.deallocate();
     897           75 :     ValRetAPaths.deallocate();
     898           75 :     ValSupAPaths.deallocate();
     899           75 : }
     900              : 
     901           75 : void TestSupplyAirPathIntegrity(EnergyPlusData &state, bool &ErrFound)
     902              : {
     903              : 
     904              :     // SUBROUTINE INFORMATION:
     905              :     //       AUTHOR         Linda Lawrie
     906              :     //       DATE WRITTEN   March 2003
     907              : 
     908              :     // PURPOSE OF THIS SUBROUTINE:
     909              :     // This subroutine tests supply air path integrity and displays the loop for each branch.
     910              :     // Also, input and output nodes.
     911              : 
     912              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     913           75 :     std::string PrimaryAirLoopName; // Air Loop to which this supply air path is connected
     914           75 :     Array1D_bool FoundSupplyPlenum;
     915           75 :     Array1D_bool FoundZoneSplitter;
     916           75 :     Array1D_string FoundNames;
     917           75 :     int NumErr = 0; // Error Counter //Autodesk:Init Initialization added
     918              : 
     919              :     // Do by Paths
     920          150 :     ShowMessage(state, "Testing Individual Supply Air Path Integrity");
     921           75 :     ErrFound = false;
     922              : 
     923           75 :     print(state.files.bnd, "{}\n", "! ===============================================================");
     924              :     static constexpr std::string_view Format_700("! <#Supply Air Paths>,<Number of Supply Air Paths>");
     925           75 :     print(state.files.bnd, "{}\n", Format_700);
     926           75 :     print(state.files.bnd, " #Supply Air Paths,{}\n", state.dataZoneEquip->NumSupplyAirPaths);
     927              :     static constexpr std::string_view Format_702("! <Supply Air Path>,<Supply Air Path Count>,<Supply Air Path Name>,<AirLoopHVAC Name>");
     928           75 :     print(state.files.bnd, "{}\n", Format_702);
     929              :     static constexpr std::string_view Format_703("! <#Components on Supply Air Path>,<Number of Components>");
     930           75 :     print(state.files.bnd, "{}\n", Format_703);
     931              :     static constexpr std::string_view Format_704(
     932              :         "! <Supply Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
     933           75 :     print(state.files.bnd, "{}\n", Format_704);
     934              :     static constexpr std::string_view Format_707("! <#Outlet Nodes on Supply Air Path Component>,<Number of Nodes>");
     935           75 :     print(state.files.bnd, "{}\n", Format_707);
     936              :     static constexpr std::string_view Format_708(
     937              :         "! <Supply Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
     938              :         "Node Name>,<AirLoopHVAC Name>");
     939           75 :     print(state.files.bnd, "{}\n", Format_708);
     940              : 
     941           99 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
     942              : 
     943              :         // Determine which air loop this supply air path is connected to
     944           24 :         int Found = 0;
     945           24 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
     946           24 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
     947           24 :             Found = 0;
     948           48 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumSupplyNodes; ++Count2) {
     949           24 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum ==
     950           24 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipSupplyNodeNum(Count2))
     951           24 :                     Found = Count2;
     952              :             }
     953           24 :             if (Found != 0) break;
     954              :         }
     955           24 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
     956              : 
     957           24 :         print(state.files.bnd, " Supply Air Path,{},{},{}\n", BCount, state.dataZoneEquip->SupplyAirPath(BCount).Name, PrimaryAirLoopName);
     958           24 :         print(state.files.bnd, "   #Components on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents);
     959              : 
     960           24 :         std::string AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).InletNodeNum);
     961              : 
     962           48 :         for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
     963              : 
     964           24 :             print(state.files.bnd,
     965              :                   "   Supply Air Path Component,{},{},{},{}\n",
     966              :                   Count,
     967           24 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     968           24 :                   state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
     969              :                   PrimaryAirLoopName);
     970              : 
     971              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
     972           24 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count))));
     973              : 
     974           24 :             switch (CompType) {
     975            0 :             case AirLoopHVACCompType::SupplyPlenum: {
     976            0 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count2) {
     977            0 :                     if (state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName !=
     978            0 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
     979            0 :                         continue;
     980            0 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode)) {
     981            0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
     982            0 :                         ShowContinueError(state,
     983            0 :                                           format("For AirLoopHVAC:SupplyPlenum={}", state.dataZonePlenum->ZoneSupPlenCond(Count2).ZonePlenumName));
     984            0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
     985            0 :                         ShowContinueError(state,
     986            0 :                                           format("Encountered node name (supply plenum)={}",
     987            0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(1))));
     988            0 :                         ErrFound = true;
     989            0 :                         ++NumErr;
     990              :                     }
     991            0 :                     print(state.files.bnd,
     992              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
     993            0 :                           state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes);
     994            0 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneSupPlenCond(Count2).NumOutletNodes; ++Count1) {
     995            0 :                         print(state.files.bnd,
     996              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
     997              :                               Count1,
     998            0 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
     999            0 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1000            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).InletNode),
    1001            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneSupPlenCond(Count2).OutletNode(Count1)),
    1002              :                               PrimaryAirLoopName);
    1003              :                     }
    1004              :                 }
    1005            0 :             } break;
    1006           24 :             case AirLoopHVACCompType::ZoneSplitter: {
    1007           48 :                 for (int Count2 = 1; Count2 <= state.dataSplitterComponent->NumSplitters; ++Count2) {
    1008           24 :                     if (state.dataSplitterComponent->SplitterCond(Count2).SplitterName !=
    1009           24 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count))
    1010            0 :                         continue;
    1011           24 :                     if (Count == 1 && AirPathNodeName != state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)) {
    1012            0 :                         ShowSevereError(state, format("Error in AirLoopHVAC:SupplyPath={}", state.dataZoneEquip->SupplyAirPath(BCount).Name));
    1013            0 :                         ShowContinueError(state,
    1014            0 :                                           format("For AirLoopHVAC:ZoneSplitter={}", state.dataSplitterComponent->SplitterCond(Count2).SplitterName));
    1015            0 :                         ShowContinueError(state, format("Expected inlet node (supply air path)={}", AirPathNodeName));
    1016            0 :                         ShowContinueError(state,
    1017            0 :                                           format("Encountered node name (zone splitter)={}",
    1018            0 :                                                  state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode)));
    1019            0 :                         ErrFound = true;
    1020            0 :                         ++NumErr;
    1021              :                     }
    1022           24 :                     print(state.files.bnd,
    1023              :                           "     #Outlet Nodes on Supply Air Path Component,{}\n",
    1024           24 :                           state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes);
    1025           61 :                     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->SplitterCond(Count2).NumOutletNodes; ++Count1) {
    1026           37 :                         print(state.files.bnd,
    1027              :                               "     Supply Air Path Component Nodes,{},{},{},{},{},{}\n",
    1028              :                               Count1,
    1029           37 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count),
    1030           37 :                               state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count),
    1031           37 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).InletNode),
    1032           37 :                               state.dataLoopNodes->NodeID(state.dataSplitterComponent->SplitterCond(Count2).OutletNode(Count1)),
    1033              :                               PrimaryAirLoopName);
    1034              :                     }
    1035              :                 }
    1036           24 :             } break;
    1037            0 :             default: {
    1038            0 :                 ShowSevereError(
    1039            0 :                     state, format("Invalid Component Type in Supply Air Path={}", state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count)));
    1040            0 :                 ErrFound = true;
    1041            0 :                 ++NumErr;
    1042            0 :             } break;
    1043              :             }
    1044              :         }
    1045              : 
    1046           24 :         if (state.dataZoneEquip->SupplyAirPath(BCount).NumNodes > 0) {
    1047              :             static constexpr std::string_view Format_705("! <#Nodes on Supply Air Path>,<Number of Nodes>");
    1048           24 :             print(state.files.bnd, "{}\n", Format_705);
    1049              :             static constexpr std::string_view Format_706("! <Supply Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1050           24 :             print(state.files.bnd, "{}\n", Format_706);
    1051           24 :             print(state.files.bnd, "#Nodes on Supply Air Path,{}\n", state.dataZoneEquip->SupplyAirPath(BCount).NumNodes);
    1052           85 :             for (int Count2 = 1; Count2 <= state.dataZoneEquip->SupplyAirPath(BCount).NumNodes; ++Count2) {
    1053           61 :                 if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::PathInlet) {
    1054           24 :                     print(state.files.bnd,
    1055              :                           "   Supply Air Path Node,Inlet Node,{},{},{}\n",
    1056              :                           Count2,
    1057           24 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1058              :                           PrimaryAirLoopName);
    1059           37 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Intermediate) {
    1060            0 :                     print(state.files.bnd,
    1061              :                           "   Supply Air Path Node,Through Node,{},{},{}\n",
    1062              :                           Count2,
    1063            0 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1064              :                           PrimaryAirLoopName);
    1065           37 :                 } else if (state.dataZoneEquip->SupplyAirPath(BCount).NodeType(Count2) == DataZoneEquipment::AirNodeType::Outlet) {
    1066           37 :                     print(state.files.bnd,
    1067              :                           "   Supply Air Path Node,Outlet Node,{},{},{}\n",
    1068              :                           Count2,
    1069           37 :                           state.dataLoopNodes->NodeID(state.dataZoneEquip->SupplyAirPath(BCount).Node(Count2)),
    1070              :                           PrimaryAirLoopName);
    1071              :                 }
    1072              :             }
    1073              :         }
    1074           24 :     }
    1075              : 
    1076           75 :     if (state.dataSplitterComponent->NumSplitters == 0) {
    1077           51 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneSplitter") > 0) {
    1078            0 :             SplitterComponent::GetSplitterInput(state);
    1079              :         }
    1080              :     }
    1081           75 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1082           75 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:SupplyPlenum") > 0) {
    1083            0 :             ZonePlenum::GetZonePlenumInput(state);
    1084              :         }
    1085              :     }
    1086              : 
    1087              :     // now the reverse.  is every zone splitter and supply plenum on supply air path
    1088           75 :     FoundSupplyPlenum.dimension(state.dataZonePlenum->NumZoneSupplyPlenums, false);
    1089           75 :     FoundZoneSplitter.dimension(state.dataSplitterComponent->NumSplitters, false);
    1090           75 :     FoundNames.allocate(state.dataZonePlenum->NumZoneSupplyPlenums);
    1091           75 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1092            0 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1093            0 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1094            0 :                 if (state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1095            0 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:SUPPLYPLENUM")
    1096            0 :                     continue;
    1097            0 :                 if (FoundSupplyPlenum(Count1)) {
    1098            0 :                     ShowSevereError(
    1099              :                         state,
    1100            0 :                         format("AirLoopHVAC:SupplyPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1101            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1102            0 :                     ErrFound = true;
    1103              :                 } else {
    1104              :                     // record use
    1105            0 :                     FoundSupplyPlenum(Count1) = true;
    1106            0 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1107              :                 }
    1108              :             }
    1109              :         }
    1110              :     }
    1111           75 :     FoundNames.deallocate();
    1112           75 :     FoundNames.allocate(state.dataSplitterComponent->NumSplitters);
    1113           99 :     for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1114           48 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumSupplyAirPaths; ++BCount) {
    1115           48 :             for (int Count = 1; Count <= state.dataZoneEquip->SupplyAirPath(BCount).NumOfComponents; ++Count) {
    1116           24 :                 if (state.dataSplitterComponent->SplitterCond(Count1).SplitterName !=
    1117           72 :                         state.dataZoneEquip->SupplyAirPath(BCount).ComponentName(Count) ||
    1118           24 :                     state.dataZoneEquip->SupplyAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONESPLITTER")
    1119            0 :                     continue;
    1120           24 :                 if (FoundZoneSplitter(Count1)) {
    1121            0 :                     ShowSevereError(
    1122              :                         state,
    1123            0 :                         format("AirLoopHVAC:ZoneSplitter=\"{}\", duplicate entry.", state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1124            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:SupplyPath=\"{}\".", FoundNames(Count1)));
    1125            0 :                     ErrFound = true;
    1126              :                 } else {
    1127              :                     // record use
    1128           24 :                     FoundZoneSplitter(Count1) = true;
    1129           24 :                     FoundNames(Count1) = state.dataZoneEquip->SupplyAirPath(BCount).Name;
    1130              :                 }
    1131              :             }
    1132              :         }
    1133              :     }
    1134           75 :     FoundNames.deallocate();
    1135              : 
    1136           75 :     if (!all(FoundSupplyPlenum)) {
    1137            0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneSupplyPlenums; ++Count1) {
    1138            0 :             if (FoundSupplyPlenum(Count1)) continue;
    1139            0 :             ShowSevereError(state,
    1140            0 :                             format("AirLoopHVAC:SupplyPlenum=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1141            0 :                                    state.dataZonePlenum->ZoneSupPlenCond(Count1).ZonePlenumName));
    1142              :         }
    1143              :     }
    1144              : 
    1145           75 :     if (!all(FoundZoneSplitter)) {
    1146            0 :         for (int Count1 = 1; Count1 <= state.dataSplitterComponent->NumSplitters; ++Count1) {
    1147            0 :             if (FoundZoneSplitter(Count1)) continue;
    1148            0 :             ShowSevereError(state,
    1149            0 :                             format("AirLoopHVAC:ZoneSplitter=\"{}\", not found on any AirLoopHVAC:SupplyPath.",
    1150            0 :                                    state.dataSplitterComponent->SplitterCond(Count1).SplitterName));
    1151              :         }
    1152              :     }
    1153              : 
    1154           75 :     FoundSupplyPlenum.deallocate();
    1155           75 :     FoundZoneSplitter.deallocate();
    1156              : 
    1157           75 :     if (ErrFound) {
    1158            0 :         ShowSevereError(state, "Supply Air Path(s) did not pass integrity testing");
    1159              :     } else {
    1160          225 :         ShowMessage(state, "All Supply Air Paths passed integrity testing");
    1161              :     }
    1162           75 : }
    1163              : 
    1164           75 : void TestReturnAirPathIntegrity(EnergyPlusData &state, bool &ErrFound, Array2S_int ValRetAPaths)
    1165              : {
    1166              : 
    1167              :     // SUBROUTINE INFORMATION:
    1168              :     //       AUTHOR         Linda Lawrie
    1169              :     //       DATE WRITTEN   March 2003
    1170              : 
    1171              :     // PURPOSE OF THIS SUBROUTINE:
    1172              :     // This subroutine tests return air path integrity and displays the loop for each branch.
    1173              :     // Also, input and output nodes.
    1174              : 
    1175              :     // REFERENCES:
    1176              :     // Return Air Path Validity Rules:
    1177              :     //  Last component (zone mixer or zone return plenum) must resolve to
    1178              :     //  be the outlet node for the return air path.  Inlets to this component must be outlets from
    1179              :     //  previous components or "controlled zone outlets"?.
    1180              :     //  (though converse not true -- each outlet in previous components do not
    1181              :     //  have to be inlets on this item -- though they must be inputs somewhere in the stream).
    1182              :     //  If multiple components and no mixer, then a zone return plenums "outlet" must
    1183              :     //  be represented as an inlet on a later plenum.  i.e. some zone return plenums are
    1184              :     //  really acting as "mixers" in a sense.  These do not need to be stepwise in succession.
    1185              :     //  Same caveat for inlets from previous item.
    1186              :     //  If multiple components and mixer, then prior condition (nested plenums) is allowed as long as
    1187              :     //  those aren't duplicated as mixer inlets.  (i.e. zone rp 1 => zone rp 2 => zone mixer but
    1188              :     //  zone rp 1 outlet should not also be inlet to mixer.
    1189              :     //  Can have (nzrp -- nested zone return plenum, pzrp -- parallel zone return plenum):
    1190              :     //  nzrp 1 => nzrp 2 & pzrp 3 => zm (inlets from nzrp 2 and pzrp 3).  Or, likewise:
    1191              :     //  pzrp 1 & pzrp 2 => zm => pzrp 3 (outlets from pzrp 1/2 are inlets to zm whose outlet is an
    1192              :     //  inlet to pzrp 3 whose outlet is the outlet for the return air path.
    1193              : 
    1194              :     //  Cannot have duplicate nodes in the "inlet" stream?  (i.e. cannot have same zone feeding two independent
    1195              :     //  plenums, for example).  Similarly, Same return plenum can't be in two air loops nor as two independent
    1196              :     //  return plenums in one return air path.
    1197              : 
    1198              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1199           75 :     std::string PrimaryAirLoopName; // Air Loop to which this return air path is connected
    1200           75 :     Array1D_bool FoundReturnPlenum;
    1201           75 :     Array1D_bool FoundZoneMixer;
    1202           75 :     Array1D_string FoundNames;
    1203           75 :     Array1D_int AllNodes;
    1204              : 
    1205              :     // Formats
    1206              : 
    1207              :     // Do by Paths
    1208          150 :     ShowMessage(state, "Testing Individual Return Air Path Integrity");
    1209           75 :     ErrFound = false;
    1210           75 :     int NumErr = 0;
    1211              : 
    1212           75 :     print(state.files.bnd, "{}\n", "! ===============================================================");
    1213              :     static constexpr std::string_view Format_700("! <#Return Air Paths>,<Number of Return Air Paths>");
    1214           75 :     print(state.files.bnd, "{}\n", Format_700);
    1215           75 :     print(state.files.bnd, " #Return Air Paths,{}\n", state.dataZoneEquip->NumReturnAirPaths);
    1216              :     static constexpr std::string_view Format_702("! <Return Air Path>,<Return Air Path Count>,<Return Air Path Name>,<AirLoopHVAC Name>");
    1217           75 :     print(state.files.bnd, "{}\n", Format_702);
    1218              :     static constexpr std::string_view Format_703("! <#Components on Return Air Path>,<Number of Components>");
    1219           75 :     print(state.files.bnd, "{}\n", Format_703);
    1220              :     static constexpr std::string_view Format_704(
    1221              :         "! <Return Air Path Component>,<Component Count>,<Component Type>,<Component Name>,<AirLoopHVAC Name>");
    1222           75 :     print(state.files.bnd, "{}\n", Format_704);
    1223              :     static constexpr std::string_view Format_707("! <#Inlet Nodes on Return Air Path Component>,<Number of Nodes>");
    1224           75 :     print(state.files.bnd, "{}\n", Format_707);
    1225              :     static constexpr std::string_view Format_708(
    1226              :         "! <Return Air Path Component Nodes>,<Node Count>,<Component Type>,<Component Name>,<Inlet Node Name>,<Outlet "
    1227              :         "Node Name>,<AirLoopHVAC Name>");
    1228           75 :     print(state.files.bnd, "{}\n", Format_708);
    1229              : 
    1230           75 :     AllNodes.allocate(state.dataLoopNodes->NumOfNodes);
    1231              : 
    1232           99 :     for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1233              :         //             Determine which air loop this supply air path is connected to
    1234           24 :         int Found = 0;
    1235           24 :         for (int Count1 = 1; Count1 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count1) {
    1236           24 :             PrimaryAirLoopName = state.dataAirLoop->AirToZoneNodeInfo(Count1).AirLoopName;
    1237           24 :             Found = 0;
    1238           48 :             for (int Count2 = 1; Count2 <= state.dataAirLoop->AirToZoneNodeInfo(Count1).NumReturnNodes; ++Count2) {
    1239           24 :                 if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum ==
    1240           24 :                     state.dataAirLoop->AirToZoneNodeInfo(Count1).ZoneEquipReturnNodeNum(Count2))
    1241           24 :                     Found = Count2;
    1242              :             }
    1243           24 :             if (Found != 0) break;
    1244              :         }
    1245           24 :         if (Found == 0) PrimaryAirLoopName = "**Unknown**";
    1246              : 
    1247           24 :         print(state.files.bnd, " Return Air Path,{},{},{}\n", BCount, state.dataZoneEquip->ReturnAirPath(BCount).Name, PrimaryAirLoopName);
    1248              : 
    1249           24 :         int NumComp = state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents;
    1250           24 :         print(state.files.bnd, "   #Components on Return Air Path,{}\n", NumComp);
    1251              : 
    1252           24 :         std::string const &AirPathNodeName = state.dataLoopNodes->NodeID(state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum);
    1253              : 
    1254           24 :         int MixerCount = 0;
    1255           48 :         for (int Count = 1; Count <= NumComp; ++Count) {
    1256           24 :             print(state.files.bnd,
    1257              :                   "   Return Air Path Component,{},{},{},{}\n",
    1258              :                   Count,
    1259           24 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count),
    1260           24 :                   state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count),
    1261              :                   PrimaryAirLoopName);
    1262              : 
    1263           24 :             if (Util::SameString(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count), "AirLoopHVAC:ZoneMixer")) {
    1264           24 :                 ++MixerCount;
    1265              :             }
    1266              :         }
    1267              : 
    1268           24 :         if (MixerCount > 1) {
    1269            0 :             ShowSevereError(state, format("Too many zone mixers in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1270            0 :             ErrFound = true;
    1271            0 :             ++NumErr;
    1272            0 :             continue;
    1273              :         }
    1274              : 
    1275           24 :         AllNodes = 0;
    1276           24 :         int CountNodes = 0;
    1277              : 
    1278           24 :         if (NumComp > 0) {
    1279              : 
    1280              :             AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1281           24 :                 getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp))));
    1282              : 
    1283           24 :             switch (CompType) {
    1284           24 :             case AirLoopHVACCompType::ZoneMixer: {
    1285           54 :                 for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1286           30 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1287            6 :                         continue;
    1288              :                     // Found correct Mixer (by name), check outlet node vs. return air path outlet node
    1289           24 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)) {
    1290            0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1291            0 :                         ShowContinueError(state, format("For Connector:Mixer={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1292            0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1293            0 :                         ShowContinueError(state,
    1294            0 :                                           format("Encountered node name (mixer)={}",
    1295            0 :                                                  state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode)));
    1296            0 :                         ErrFound = true;
    1297            0 :                         ++NumErr;
    1298              :                     } else {
    1299           24 :                         ++CountNodes;
    1300           24 :                         AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).OutletNode;
    1301           24 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataMixerComponent->MixerCond(Count2).OutletNode) {
    1302           24 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1303              :                         }
    1304           61 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1305           37 :                             ++CountNodes;
    1306           37 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1307              :                         }
    1308              :                     }
    1309           24 :                     print(state.files.bnd,
    1310              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1311           24 :                           state.dataMixerComponent->MixerCond(Count2).NumInletNodes);
    1312           61 :                     for (int Count1 = 1; Count1 <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Count1) {
    1313           37 :                         print(state.files.bnd,
    1314              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1315              :                               Count1,
    1316           37 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1317           37 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1318           37 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).InletNode(Count1)),
    1319           37 :                               state.dataLoopNodes->NodeID(state.dataMixerComponent->MixerCond(Count2).OutletNode),
    1320              :                               PrimaryAirLoopName);
    1321              :                     }
    1322              :                 }
    1323           24 :             } break;
    1324            0 :             case AirLoopHVACCompType::ReturnPlenum: {
    1325            0 :                 for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1326            0 :                     if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp) !=
    1327            0 :                         state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1328            0 :                         continue;
    1329            0 :                     if (AirPathNodeName != state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)) {
    1330            0 :                         ShowSevereError(state, format("Error in Return Air Path={}", state.dataZoneEquip->ReturnAirPath(BCount).Name));
    1331            0 :                         ShowContinueError(
    1332            0 :                             state, format("For AirLoopHVAC:ReturnPlenum={}", state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp)));
    1333            0 :                         ShowContinueError(state, format("Expected outlet node (return air path)={}", AirPathNodeName));
    1334            0 :                         ShowContinueError(state,
    1335            0 :                                           format("Encountered node name (zone return plenum)={}",
    1336            0 :                                                  state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode)));
    1337            0 :                         ErrFound = true;
    1338            0 :                         ++NumErr;
    1339              :                     } else {
    1340            0 :                         ++CountNodes;
    1341            0 :                         AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode;
    1342            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).OutletNodeNum == state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode) {
    1343            0 :                             state.dataZoneEquip->ReturnAirPath(BCount).OutletRetPathCompNum = NumComp;
    1344              :                         }
    1345            0 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1346            0 :                             ++CountNodes;
    1347            0 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1348              :                         }
    1349              :                     }
    1350            0 :                     print(state.files.bnd,
    1351              :                           "     #Inlet Nodes on Return Air Path Component,{}\n",
    1352            0 :                           state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes);
    1353            0 :                     for (int Count1 = 1; Count1 <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Count1) {
    1354            0 :                         print(state.files.bnd,
    1355              :                               "     Return Air Path Component Nodes,{},{},{},{},{},{}\n",
    1356              :                               Count1,
    1357            0 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(NumComp),
    1358            0 :                               state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(NumComp),
    1359            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Count1)),
    1360            0 :                               state.dataLoopNodes->NodeID(state.dataZonePlenum->ZoneRetPlenCond(Count2).OutletNode),
    1361              :                               PrimaryAirLoopName);
    1362              :                     }
    1363              :                 }
    1364            0 :             } break;
    1365            0 :             default: // This already validated in GetReturnAirPath
    1366            0 :                 break;
    1367              :             }
    1368              :         }
    1369              : 
    1370           24 :         if (NumComp > 1) {
    1371            0 :             for (int Count3 = 1; Count3 <= NumComp - 1; ++Count3) {
    1372              : 
    1373              :                 AirLoopHVACCompType CompType = static_cast<AirLoopHVACCompType>(
    1374            0 :                     getEnumValue(AirLoopHVACCompTypeNamesUC, Util::makeUPPER(state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count3))));
    1375              : 
    1376            0 :                 switch (CompType) {
    1377            0 :                 case AirLoopHVACCompType::ZoneMixer: {
    1378            0 :                     for (int Count2 = 1; Count2 <= state.dataMixerComponent->NumMixers; ++Count2) {
    1379            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) != state.dataMixerComponent->MixerCond(Count2).MixerName)
    1380            0 :                             continue;
    1381            0 :                         for (int Loop = 1; Loop <= state.dataMixerComponent->MixerCond(Count2).NumInletNodes; ++Loop) {
    1382            0 :                             ++CountNodes;
    1383            0 :                             AllNodes(CountNodes) = state.dataMixerComponent->MixerCond(Count2).InletNode(Loop);
    1384              :                         }
    1385              :                     }
    1386            0 :                 } break;
    1387            0 :                 case AirLoopHVACCompType::ReturnPlenum: {
    1388            0 :                     for (int Count2 = 1; Count2 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count2) {
    1389            0 :                         if (state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count3) !=
    1390            0 :                             state.dataZonePlenum->ZoneRetPlenCond(Count2).ZonePlenumName)
    1391            0 :                             continue;
    1392            0 :                         for (int Loop = 1; Loop <= state.dataZonePlenum->ZoneRetPlenCond(Count2).NumInletNodes; ++Loop) {
    1393            0 :                             ++CountNodes;
    1394            0 :                             AllNodes(CountNodes) = state.dataZonePlenum->ZoneRetPlenCond(Count2).InletNode(Loop);
    1395              :                         }
    1396              :                     }
    1397            0 :                 } break;
    1398            0 :                 default: // This already validated in GetReturnAirPath
    1399            0 :                     break;
    1400              :                 }
    1401              :             }
    1402              :         }
    1403           24 :         if (CountNodes > 0) {
    1404              :             static constexpr std::string_view Format_705("! <#Nodes on Return Air Path>,<Number of Nodes>");
    1405           24 :             print(state.files.bnd, "{}\n", Format_705);
    1406              :             static constexpr std::string_view Format_706("! <Return Air Path Node>,<Node Type>,<Node Count>,<Node Name>,<AirLoopHVAC Name>");
    1407           24 :             print(state.files.bnd, "{}\n", Format_706);
    1408           24 :             print(state.files.bnd, "   #Nodes on Return Air Path,{}\n", CountNodes);
    1409           85 :             for (int Count2 = 1; Count2 <= CountNodes; ++Count2) {
    1410           61 :                 if (Count2 == 1) {
    1411           24 :                     print(state.files.bnd,
    1412              :                           "   Return Air Path Node,Outlet Node,{},{},{}\n",
    1413              :                           Count2,
    1414           24 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1415              :                           PrimaryAirLoopName);
    1416              :                 } else {
    1417           37 :                     print(state.files.bnd,
    1418              :                           "   Return Air Path Node,Inlet Node,{},{},{}\n",
    1419              :                           Count2,
    1420           37 :                           state.dataLoopNodes->NodeID(AllNodes(Count2)),
    1421              :                           PrimaryAirLoopName);
    1422              :                 }
    1423              :             }
    1424              :         }
    1425              :         // Determine Air Loop this Return Air Path is on
    1426           24 :         for (int Count2 = 1; Count2 <= state.dataHVACGlobal->NumPrimaryAirSys; ++Count2) {
    1427           24 :             if (state.dataAirLoop->AirToZoneNodeInfo(Count2).NumReturnNodes > 0) {
    1428           24 :                 if (AllNodes(1) == state.dataAirLoop->AirToZoneNodeInfo(Count2).ZoneEquipReturnNodeNum(1)) {
    1429           24 :                     const int WAirLoop = Count2;
    1430           24 :                     ValRetAPaths(_, WAirLoop) = 0;
    1431           24 :                     ValRetAPaths({1, CountNodes}, WAirLoop) = AllNodes({1, CountNodes});
    1432           24 :                     break;
    1433              :                 }
    1434              :             } else {
    1435            0 :                 ShowWarningError(state,
    1436            0 :                                  format("TestReturnAirPathIntegrity: Air Loop has no Zone Equipment Return Node={}",
    1437            0 :                                         state.dataAirLoop->AirToZoneNodeInfo(Count2).AirLoopName));
    1438              :             }
    1439              :         }
    1440              :     }
    1441              : 
    1442           75 :     AllNodes.deallocate();
    1443              : 
    1444           75 :     if (state.dataMixerComponent->NumMixers == 0) {
    1445           51 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ZoneMixer") > 0) {
    1446            0 :             MixerComponent::GetMixerInput(state);
    1447              :         }
    1448              :     }
    1449           75 :     if (state.dataZonePlenum->NumZoneSupplyPlenums == 0 && state.dataZonePlenum->NumZoneReturnPlenums == 0) {
    1450           75 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirLoopHVAC:ReturnPlenum") > 0) {
    1451            0 :             ZonePlenum::GetZonePlenumInput(state);
    1452              :         }
    1453              :     }
    1454              : 
    1455              :     // now the reverse.  is every zone Mixer and Return plenum on Return air path
    1456           75 :     FoundReturnPlenum.dimension(state.dataZonePlenum->NumZoneReturnPlenums, false);
    1457           75 :     FoundZoneMixer.dimension(state.dataMixerComponent->NumMixers, false);
    1458           75 :     FoundNames.allocate(state.dataZonePlenum->NumZoneReturnPlenums);
    1459           75 :     for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1460            0 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1461            0 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1462            0 :                 if (state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1463            0 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:RETURNPLENUM")
    1464            0 :                     continue;
    1465            0 :                 if (FoundReturnPlenum(Count1)) {
    1466            0 :                     ShowSevereError(
    1467              :                         state,
    1468            0 :                         format("AirLoopHVAC:ReturnPlenum=\"{}\", duplicate entry.", state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1469            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1470            0 :                     ErrFound = true;
    1471              :                 } else {
    1472              :                     // record use
    1473            0 :                     FoundReturnPlenum(Count1) = true;
    1474            0 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1475              :                 }
    1476              :             }
    1477              :         }
    1478            0 :         if (PurchasedAirManager::CheckPurchasedAirForReturnPlenum(state, Count1)) FoundReturnPlenum(Count1) = true;
    1479              :     }
    1480           75 :     FoundNames.deallocate();
    1481           75 :     FoundNames.allocate(state.dataMixerComponent->NumMixers);
    1482          105 :     for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1483           60 :         for (int BCount = 1; BCount <= state.dataZoneEquip->NumReturnAirPaths; ++BCount) {
    1484           60 :             for (int Count = 1; Count <= state.dataZoneEquip->ReturnAirPath(BCount).NumOfComponents; ++Count) {
    1485           54 :                 if (state.dataMixerComponent->MixerCond(Count1).MixerName != state.dataZoneEquip->ReturnAirPath(BCount).ComponentName(Count) ||
    1486           24 :                     state.dataZoneEquip->ReturnAirPath(BCount).ComponentType(Count) != "AIRLOOPHVAC:ZONEMIXER")
    1487            6 :                     continue;
    1488           24 :                 if (FoundZoneMixer(Count1)) {
    1489            0 :                     ShowSevereError(state,
    1490            0 :                                     format("AirLoopHVAC:ZoneMixer=\"{}\", duplicate entry.", state.dataMixerComponent->MixerCond(Count1).MixerName));
    1491            0 :                     ShowContinueError(state, format("already exists on AirLoopHVAC:ReturnPath=\"{}\".", FoundNames(Count1)));
    1492            0 :                     ErrFound = true;
    1493              :                 } else {
    1494              :                     // record use
    1495           24 :                     FoundZoneMixer(Count1) = true;
    1496           24 :                     FoundNames(Count1) = state.dataZoneEquip->ReturnAirPath(BCount).Name;
    1497              :                 }
    1498              :             }
    1499              :         }
    1500           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1501              :             // PIU Units
    1502            6 :             if (PoweredInductionUnits::PIUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName)) FoundZoneMixer(Count1) = true;
    1503              :         }
    1504           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1505              :             // fourPipeInduction units
    1506            0 :             if (HVACSingleDuctInduc::FourPipeInductionUnitHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
    1507            0 :                 FoundZoneMixer(Count1) = true;
    1508              :         }
    1509           30 :         if (!FoundZoneMixer(Count1)) { // could be as child on other items
    1510              :             // Exhaust Systems
    1511            0 :             if (ExhaustAirSystemManager::ExhaustSystemHasMixer(state, state.dataMixerComponent->MixerCond(Count1).MixerName))
    1512            0 :                 FoundZoneMixer(Count1) = true;
    1513              :         }
    1514              :     }
    1515           75 :     FoundNames.deallocate();
    1516              : 
    1517           75 :     if (!all(FoundReturnPlenum)) {
    1518            0 :         for (int Count1 = 1; Count1 <= state.dataZonePlenum->NumZoneReturnPlenums; ++Count1) {
    1519            0 :             if (FoundReturnPlenum(Count1)) continue;
    1520            0 :             ShowSevereError(state,
    1521            0 :                             format("AirLoopHVAC:ReturnPlenum=\"{}\", not found on any AirLoopHVAC:ReturnPath.",
    1522            0 :                                    state.dataZonePlenum->ZoneRetPlenCond(Count1).ZonePlenumName));
    1523              :         }
    1524              :     }
    1525              : 
    1526           75 :     if (!all(FoundZoneMixer)) {
    1527            0 :         for (int Count1 = 1; Count1 <= state.dataMixerComponent->NumMixers; ++Count1) {
    1528            0 :             if (FoundZoneMixer(Count1)) continue;
    1529            0 :             ShowSevereError(state,
    1530            0 :                             format("AirLoopHVAC:ZoneMixer=\"{}\", not found on any AirLoopHVAC:ReturnPath, AirLoopHVAC:ExhaustSystem, "
    1531              :                                    "AirTerminal:SingleDuct:SeriesPIU:Reheat,",
    1532            0 :                                    state.dataMixerComponent->MixerCond(Count1).MixerName));
    1533            0 :             ShowContinueError(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat or AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction.");
    1534              :         }
    1535              :     }
    1536              : 
    1537           75 :     FoundReturnPlenum.deallocate();
    1538           75 :     FoundZoneMixer.deallocate();
    1539              : 
    1540           75 :     if (ErrFound) {
    1541            0 :         ShowSevereError(state, "Return Air Path(s) did not pass integrity testing");
    1542              :     } else {
    1543          225 :         ShowMessage(state, "All Return Air Paths passed integrity testing");
    1544              :     }
    1545           75 : }
    1546              : 
    1547       296531 : void CalcComponentSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1548              :                                        Real64 const TDB2,      // dry-bulb temperature at state 2 {C}
    1549              :                                        Real64 const W2,        // humidity ratio at state 2
    1550              :                                        Real64 const TDB1,      // dry-bulb temperature at  at state 1 {C}
    1551              :                                        Real64 const W1,        // humidity ratio at state 1
    1552              :                                        Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1553              :                                        Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1554              :                                        Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1555              : )
    1556              : {
    1557              : 
    1558              :     // Purpose:
    1559              :     // returns total, sensible and latent heat rate of change of moist air transitioning
    1560              :     // between two states. The moist air energy transfer can be cooling or heating process
    1561              :     // across a cooling, a heating coil, or an HVAC component.
    1562              : 
    1563              :     // Methodology:
    1564              :     // Q_total = m_dot * (h2 - h1)
    1565              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1);
    1566              :     // or Q_sensible = m_dot * cp_moistair_MinHumRat * (TDB2 - TDB1)
    1567              :     //    cp_moistair_MinHumRat = Psychrometrics::PsyCpAirFnW(min(W2, W1));
    1568              :     // Q_latent = Q_total - Q_latent;
    1569              : 
    1570       296531 :     TotalOutput = 0.0;
    1571       296531 :     LatentOutput = 0.0;
    1572       296531 :     SensibleOutput = 0.0;
    1573       296531 :     if (MassFlow > 0.0) {
    1574       276105 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDB2, W2) - Psychrometrics::PsyHFnTdbW(TDB1, W1)); // total addition/removal rate, {W};
    1575       276105 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2W2Tdb1W1(TDB2, W2, TDB1, W1); // sensible addition/removal rate, {W};
    1576       276105 :         LatentOutput = TotalOutput - SensibleOutput;                                                // latent addition/removal rate, {W}
    1577              :     }
    1578       296531 : }
    1579              : 
    1580       195790 : void CalcZoneSensibleLatentOutput(Real64 const MassFlow,  // air mass flow rate, {kg/s}
    1581              :                                   Real64 const TDBEquip,  // dry-bulb temperature at equipment outlet {C}
    1582              :                                   Real64 const WEquip,    // humidity ratio at equipment outlet
    1583              :                                   Real64 const TDBZone,   // dry-bulb temperature at zone air node {C}
    1584              :                                   Real64 const WZone,     // humidity ratio at zone air node
    1585              :                                   Real64 &SensibleOutput, // sensible output rate (state 2 -> State 1), {W}
    1586              :                                   Real64 &LatentOutput,   // latent output rate (state 2 -> State 1), {W}
    1587              :                                   Real64 &TotalOutput     // total = sensible + latent putput rate (state 2 -> State 1), {W}
    1588              : )
    1589              : {
    1590              : 
    1591              :     // Purpose:
    1592              :     // returns total, sensible and latent heat rate of transfer between the supply air zone inlet
    1593              :     // node and zone air node. The moist air energy transfer can be cooling or heating depending
    1594              :     // on the supply air zone inlet node and zone air node conditions.
    1595              : 
    1596              :     // Methodology:
    1597              :     // Q_total = m_dot * (hEquip - hZone)
    1598              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1599              :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1600              :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1601              :     // Q_latent = Q_total - Q_latent;
    1602              : 
    1603       195790 :     TotalOutput = 0.0;
    1604       195790 :     LatentOutput = 0.0;
    1605       195790 :     SensibleOutput = 0.0;
    1606       195790 :     if (MassFlow > 0.0) {
    1607       141050 :         TotalOutput = MassFlow * (Psychrometrics::PsyHFnTdbW(TDBEquip, WEquip) -
    1608       141050 :                                   Psychrometrics::PsyHFnTdbW(TDBZone, WZone));                         // total addition/removal rate, {W};
    1609       141050 :         SensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1610       141050 :         LatentOutput = TotalOutput - SensibleOutput;                                                   // latent addition/removal rate, {W}
    1611              :     }
    1612       195790 : }
    1613              : 
    1614       401829 : Real64 calcZoneSensibleOutput(Real64 const MassFlow, // air mass flow rate, {kg/s}
    1615              :                               Real64 const TDBEquip, // dry-bulb temperature at equipment outlet {C}
    1616              :                               Real64 const TDBZone,  // dry-bulb temperature at zone air node {C}
    1617              :                               Real64 const WZone     // humidity ratio at zone air node
    1618              : )
    1619              : {
    1620              : 
    1621              :     // Purpose:
    1622              :     // returns sensible heat rate of transfer between the supply air zone inlet node and
    1623              :     // zone air node. The moist air energy transfer can be cooling or heating depending
    1624              :     // on the supply air zone inlet node and zone air node conditions.
    1625              : 
    1626              :     // Methodology:
    1627              :     // Q_sensible = m_dot * Psychrometrics::PsyDeltaHSenFnTdbEquipTdbWZone(TDBEquip, TDBZone, WZone);
    1628              :     // or Q_sensible = m_dot * cp_moistair_zoneHumRat * (TDBEquip - TDBZone)
    1629              :     //    cp_moistair_zoneHumRat = Psychrometrics::PsyCpAirFnW(WZone);
    1630              : 
    1631       401829 :     Real64 sensibleOutput = 0.0; // sensible output rate (state 2 -> State 1), {W}
    1632       401829 :     if (MassFlow > 0.0) {
    1633       322958 :         sensibleOutput = MassFlow * Psychrometrics::PsyDeltaHSenFnTdb2Tdb1W(TDBEquip, TDBZone, WZone); // sensible addition/removal rate, {W};
    1634              :     }
    1635       401829 :     return sensibleOutput;
    1636              : }
    1637              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1