LCOV - code coverage report
Current view: top level - EnergyPlus - SZVAVModel.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 63.4 % 440 279
Test Date: 2025-05-22 16:09:37 Functions: 52.9 % 17 9

            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 <cassert>
      50              : #include <cmath>
      51              : #include <cstdlib>
      52              : 
      53              : // ObjexxFCL Headers
      54              : #include <ObjexxFCL/string.functions.hh>
      55              : 
      56              : // EnergyPlus Headers
      57              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58              : #include <EnergyPlus/DataHVACGlobals.hh>
      59              : #include <EnergyPlus/DataLoopNode.hh>
      60              : #include <EnergyPlus/General.hh>
      61              : #include <EnergyPlus/PlantUtilities.hh>
      62              : #include <EnergyPlus/Psychrometrics.hh>
      63              : #include <EnergyPlus/SZVAVModel.hh>
      64              : #include <EnergyPlus/UnitarySystem.hh>
      65              : #include <EnergyPlus/UtilityRoutines.hh>
      66              : 
      67              : namespace EnergyPlus {
      68              : 
      69              : namespace SZVAVModel {
      70              : 
      71              :     // Module containing routines for general use
      72              : 
      73              :     // Data
      74              :     // This module should not contain variables in the module sense as it is
      75              :     // intended strictly to provide "interfaces" to routines used by other
      76              :     // parts of the simulation.
      77              : 
      78              :     // MODULE PARAMETER DEFINITIONS
      79              : 
      80              :     // Functions
      81              : 
      82           11 :     void calcSZVAVModel(EnergyPlusData &state,
      83              :                         FanCoilUnits::FanCoilData &SZVAVModel,
      84              :                         int const SysIndex,
      85              :                         bool const FirstHVACIteration,
      86              :                         bool const CoolingLoad,
      87              :                         bool const HeatingLoad,
      88              :                         Real64 const ZoneLoad,
      89              :                         [[maybe_unused]] Real64 &OnOffAirFlowRatio,
      90              :                         [[maybe_unused]] bool const HXUnitOn,
      91              :                         [[maybe_unused]] int const AirLoopNum,
      92              :                         Real64 &PartLoadRatio,
      93              :                         [[maybe_unused]] HVAC::CompressorOp const CompressorONFlag)
      94              :     {
      95              : 
      96           11 :         int constexpr MaxIter(100); // maximum number of iterations
      97           11 :         int SolFlag(0);             // return flag from RegulaFalsi for sensible load
      98           11 :         std::string MessagePrefix;  // label for warning reporting
      99              : 
     100           11 :         Real64 lowBoundaryLoad(0.0);
     101           11 :         Real64 highBoundaryLoad(0.0);
     102           11 :         Real64 minHumRat(0.0);
     103           11 :         Real64 outletTemp(0.0);
     104           11 :         bool coilActive(false);
     105           11 :         Real64 AirMassFlow(0.0);
     106              : 
     107           11 :         Real64 maxCoilFluidFlow(0.0);
     108           11 :         Real64 maxOutletTemp(0.0);
     109           11 :         Real64 minAirMassFlow(0.0);
     110           11 :         Real64 maxAirMassFlow(0.0);
     111           11 :         Real64 lowSpeedFanRatio(0.0);
     112           11 :         int coilFluidInletNode(0);
     113           11 :         int coilFluidOutletNode(0);
     114           11 :         PlantLocation coilPlantLoc{};
     115           11 :         int coilAirInletNode(0);
     116           11 :         int coilAirOutletNode(0);
     117              : 
     118              :         Real64 TempSensOutput; // iterative sensible capacity [W]
     119              :                                //        Real64 TempLatOutput; // iterative latent capacity [W]
     120              : 
     121              :         // set up mode specific variables to use in common function calls
     122           11 :         if (CoolingLoad) {
     123            4 :             maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
     124            4 :             maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
     125            4 :             minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
     126            4 :             maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
     127            4 :             lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
     128            4 :             coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
     129            4 :             coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
     130            4 :             coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
     131            4 :             coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
     132            4 :             coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
     133            7 :         } else if (HeatingLoad) {
     134            7 :             maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
     135            7 :             maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
     136            7 :             minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
     137            7 :             maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
     138            7 :             lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
     139            7 :             coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
     140            7 :             coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
     141            7 :             coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
     142            7 :             coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
     143            7 :             coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
     144              :         } else { // should never get here, protect against uninitialized variables
     145            0 :             maxCoilFluidFlow = 0.0;
     146            0 :             maxOutletTemp = 0.0;
     147            0 :             minAirMassFlow = 0.0;
     148            0 :             maxAirMassFlow = 0.0;
     149            0 :             lowSpeedFanRatio = 0.0;
     150            0 :             coilFluidInletNode = 0;
     151            0 :             coilFluidOutletNode = 0;
     152            0 :             coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
     153            0 :             coilAirInletNode = 0;
     154            0 :             coilAirOutletNode = 0;
     155              :         }
     156           11 :         int InletNode = SZVAVModel.AirInNode;
     157           11 :         Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
     158           11 :         int OutletNode = SZVAVModel.AirOutNode;
     159           11 :         Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
     160           11 :         Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
     161              :         // initialize flow variables to 0
     162           11 :         Real64 lowWaterMdot = 0.0;
     163              :         // Real64 SupHeaterLoad = 0.0;
     164              : 
     165              :         // model attempts to control air flow rate and coil capacity in specific operating regions:
     166              :         // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
     167              :         // Region 2 (R2) - modulated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
     168              :         // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
     169              :         // remaining load)
     170              :         //
     171              :         //                |    |                   |    |    ^            ^ = supply air temperature
     172              :         //                |    |                   |    | ^               * = supply air flow rate
     173              :         //                |    |                   |^^^^| <--- maximum supply air temperature
     174              :         //                |    |                ^  |    |
     175              :         //                |    |              ^    |    |
     176              :         //     ***********|    |            ^      |    |**************   <-- max unit air flow rate
     177              :         //                |*   |          ^        |   *|
     178              :         //                | *  |        ^          |  * |
     179              :         //                |  * |      ^            | *  |
     180              :         //                |   *|    ^              |*   |
     181              :         //                |    |*******************|    |                 <-- min unit air flow rate
     182              :         //          R3    | R2 | ^       R1        | R2 |    R3
     183              :         //   min SAT -->  |^^^^|                   |    |
     184              :         //               ^
     185              :         //             ^                  |
     186              :         //  (-) increasing cooling load < 0 > increasing heating load (+)
     187              :         //
     188              :         // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
     189              :         //        Load here is not the zone load, it's the load the system must meet to meet the Tstat set point (i.e., OA can alter required
     190              :         //        capacity) lowSpeedFanRatio = min/max unit air flow rate
     191              :         //
     192              :         // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
     193              :         //         - if load can be met, test maximum output (PLR = 1) before calling RootSolver
     194              :         //         - if maximum capacity is greater than load, solve for PLR
     195              :         // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
     196              :         //         - if load is less than boundary load, solve for air flow and PLR that meet the load
     197              :         //         - ELSE
     198              :         // Step 3: solve for Region 3 PLR
     199              :         //       DONE
     200              :         //
     201              : 
     202              :         // Step 1: Determine boundary for region 1
     203              :         // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
     204           11 :         if (SZVAVModel.ATMixerExists) {
     205            0 :             if (SZVAVModel.ATMixerType == HVAC::MixerType::SupplySide) {
     206              :                 // Air terminal supply side mixer
     207            0 :                 lowBoundaryLoad =
     208            0 :                     minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
     209            0 :                                       Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
     210              :             } else {
     211              :                 // Air terminal inlet side mixer
     212            0 :                 lowBoundaryLoad =
     213            0 :                     minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
     214              :             }
     215              :         } else {
     216           11 :             minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
     217           11 :             lowBoundaryLoad =
     218           11 :                 minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
     219              :         }
     220              : 
     221           11 :         if ((CoolingLoad && lowBoundaryLoad < ZoneLoad) || (HeatingLoad && lowBoundaryLoad > ZoneLoad)) { // in Region 1 of figure
     222              :             // Step 1: set min air flow and full coil capacity
     223            2 :             PartLoadRatio = 1.0; // full coil capacity
     224            2 :             SZVAVModel.FanPartLoadRatio =
     225              :                 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
     226            2 :             state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
     227              :             // set max water flow rate and check to see if plant limits flow
     228            2 :             if (coilPlantLoc.loopNum > 0)
     229            2 :                 PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     230              : 
     231            2 :             if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
     232              :                 // set the water flow ratio so water coil gets proper flow
     233            1 :                 if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
     234              :             }
     235            2 :             FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
     236            2 :             coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
     237              : 
     238            2 :             if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
     239            0 :                 if (coilPlantLoc.loopNum > 0) {
     240            0 :                     state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
     241            0 :                     PlantUtilities::SetComponentFlowRate(
     242            0 :                         state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     243              :                 }
     244            0 :                 return;
     245              :             }
     246              : 
     247            2 :             if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
     248              : 
     249           19 :                 auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
     250              :                              Real64 const PLR) {
     251           38 :                     return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
     252              :                                                                       PLR,
     253              :                                                                       SysIndex,
     254              :                                                                       FirstHVACIteration,
     255           19 :                                                                       SZVAVModel.ControlZoneNum,
     256              :                                                                       ZoneLoad,
     257           19 :                                                                       SZVAVModel.AirInNode,
     258              :                                                                       coilFluidInletNode,
     259              :                                                                       maxCoilFluidFlow,
     260           19 :                                                                       minAirMassFlow);
     261            2 :                 };
     262            2 :                 General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     263            2 :                 if (SolFlag < 0) {
     264            0 :                     MessagePrefix = "Step 1: ";
     265              :                 }
     266              : 
     267            2 :                 if (coilPlantLoc.loopNum > 0)
     268            2 :                     PlantUtilities::SetComponentFlowRate(
     269            2 :                         state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     270              :             }
     271              : 
     272            2 :         } else {
     273              : 
     274              :             // Step 2: Load is greater then allowed in region 1, determine boundary load for region 3
     275              :             // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
     276              :             // calculation
     277            9 :             highBoundaryLoad = lowBoundaryLoad * maxAirMassFlow / minAirMassFlow;
     278              : 
     279            9 :             if ((CoolingLoad && highBoundaryLoad < ZoneLoad) || (HeatingLoad && highBoundaryLoad > ZoneLoad)) { // in Region 2 of figure
     280              : 
     281            2 :                 outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
     282            2 :                 minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
     283            2 :                 if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
     284            2 :                 outletTemp = maxOutletTemp;
     285            2 :                 AirMassFlow = min(maxAirMassFlow,
     286            2 :                                   (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
     287            2 :                 AirMassFlow = max(minAirMassFlow, AirMassFlow);
     288            2 :                 SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
     289              : 
     290            2 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate = AirMassFlow;
     291              :                 // does unit have capacity less than load at this air flow rate
     292            2 :                 if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = lowWaterMdot;
     293            2 :                 FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 0.0);
     294            2 :                 if ((CoolingLoad && (TempSensOutput > ZoneLoad)) || (HeatingLoad && (TempSensOutput < ZoneLoad))) {
     295              :                     // can unit get there with max water flow?
     296            2 :                     if (coilFluidInletNode > 0) state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = maxCoilFluidFlow;
     297            2 :                     FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, 1.0);
     298              : 
     299              :                     // set max water flow rate and check to see if plant limits flow
     300            2 :                     if (coilPlantLoc.loopNum > 0)
     301            2 :                         PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     302              : 
     303            2 :                     if ((CoolingLoad && (TempSensOutput < ZoneLoad)) || (HeatingLoad && (TempSensOutput > ZoneLoad))) {
     304            2 :                         if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
     305           20 :                             auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, AirMassFlow](
     306              :                                          Real64 const PLR) {
     307           40 :                                 return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
     308              :                                                                                   PLR,
     309              :                                                                                   SysIndex,
     310              :                                                                                   FirstHVACIteration,
     311           20 :                                                                                   SZVAVModel.ControlZoneNum,
     312              :                                                                                   ZoneLoad,
     313           20 :                                                                                   SZVAVModel.AirInNode,
     314              :                                                                                   coilFluidInletNode,
     315              :                                                                                   maxCoilFluidFlow,
     316           20 :                                                                                   AirMassFlow);
     317            2 :                             };
     318            2 :                             General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     319            2 :                         } else {
     320            0 :                             auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
     321            0 :                                 return FanCoilUnits::CalcFanCoilLoadResidual(
     322            0 :                                     state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
     323            0 :                             };
     324            0 :                             General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     325              :                         }
     326            2 :                         outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
     327              :                         if ((CoolingLoad && outletTemp < maxOutletTemp) || (HeatingLoad && outletTemp > maxOutletTemp)) {
     328              :                             // must do something here to maintain outlet temp while in Region 2
     329              :                         }
     330            2 :                         if (SolFlag < 0) {
     331            0 :                             MessagePrefix = "Step 2: ";
     332              :                         }
     333            2 :                     } else { // not enough capacity at this air flow rate. Unit does have enough capacity a full water/air, otherwise wouldn't be here
     334              :                         // this is different from the PTUnit and UnitarySys routines in this module
     335              :                         // find the water flow rate that meets the min load at region 1/2 boundary
     336            0 :                         if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
     337              :                             auto f = // (AUTO_OK_LAMBDA)
     338            0 :                                 [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, minAirMassFlow](
     339              :                                     Real64 const PLR) {
     340            0 :                                     return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
     341              :                                                                                       PLR,
     342              :                                                                                       SysIndex,
     343              :                                                                                       FirstHVACIteration,
     344            0 :                                                                                       SZVAVModel.ControlZoneNum,
     345              :                                                                                       ZoneLoad,
     346            0 :                                                                                       SZVAVModel.AirInNode,
     347              :                                                                                       coilFluidInletNode,
     348              :                                                                                       maxCoilFluidFlow,
     349            0 :                                                                                       minAirMassFlow);
     350            0 :                                 };
     351            0 :                             General::SolveRoot(state, 0.001, MaxIter, SolFlag, lowWaterMdot, f, 0.0, 1.0);
     352            0 :                             Real64 minFlow = lowWaterMdot;
     353            0 :                             if (SolFlag < 0) {
     354            0 :                                 MessagePrefix = "Step 2a: ";
     355              :                             } else {
     356            0 :                                 minFlow = 0.0;
     357              :                             }
     358            0 :                             auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, minFlow](Real64 const PLR) {
     359            0 :                                 return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
     360              :                                                                                         PLR,
     361              :                                                                                         SysIndex,
     362              :                                                                                         FirstHVACIteration,
     363            0 :                                                                                         SZVAVModel.ControlZoneNum,
     364              :                                                                                         ZoneLoad,
     365            0 :                                                                                         SZVAVModel.AirInNode,
     366              :                                                                                         coilFluidInletNode,
     367            0 :                                                                                         minFlow);
     368            0 :                             };
     369            0 :                             General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
     370            0 :                             if (SolFlag < 0) {
     371            0 :                                 MessagePrefix = "Step 2b: ";
     372              :                             }
     373            0 :                         } else {
     374            0 :                             auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
     375            0 :                                 return FanCoilUnits::CalcFanCoilLoadResidual(
     376            0 :                                     state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
     377            0 :                             };
     378            0 :                             General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     379            0 :                             if (SolFlag < 0) {
     380            0 :                                 MessagePrefix = "Step 2: ";
     381              :                             }
     382              :                         }
     383              :                     }
     384            2 :                 } else { // too much capacity when coil off, could lower air flow rate here to meet load if air flow is above minimum
     385            0 :                     if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
     386            0 :                         auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
     387            0 :                             return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
     388              :                                                                                     PLR,
     389              :                                                                                     SysIndex,
     390              :                                                                                     FirstHVACIteration,
     391            0 :                                                                                     SZVAVModel.ControlZoneNum,
     392              :                                                                                     ZoneLoad,
     393            0 :                                                                                     SZVAVModel.AirInNode,
     394              :                                                                                     coilFluidInletNode,
     395            0 :                                                                                     0.0);
     396            0 :                         };
     397            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
     398            0 :                     } else {
     399            0 :                         auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
     400            0 :                             return FanCoilUnits::CalcFanCoilLoadResidual(
     401            0 :                                 state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
     402            0 :                         };
     403            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     404              :                     }
     405            0 :                     if (SolFlag < 0) {
     406            0 :                         MessagePrefix = "Step 2c: ";
     407              :                     }
     408              :                 }
     409              : 
     410            2 :             } else { // in region 3 of figure
     411              : 
     412            7 :                 PartLoadRatio = 1.0; // full coil capacity
     413            7 :                 SZVAVModel.FanPartLoadRatio =
     414              :                     1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
     415            7 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
     416              :                 // set max water flow rate and check to see if plant limits flow
     417            7 :                 if (coilPlantLoc.loopNum > 0)
     418            3 :                     PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     419              : 
     420            7 :                 if (HeatingLoad) { // Function UnitarySystems::calcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
     421              :                     // set the water flow ratio so water coil gets proper flow
     422            5 :                     if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
     423              :                 }
     424            7 :                 FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
     425            7 :                 coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
     426            7 :                 if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
     427            0 :                     if (coilPlantLoc.loopNum > 0) {
     428            0 :                         state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
     429            0 :                         PlantUtilities::SetComponentFlowRate(
     430            0 :                             state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     431              :                     }
     432            0 :                     return;
     433              :                 }
     434              : 
     435            7 :                 if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
     436            0 :                     return; // system cannot meet load, leave at max capacity
     437              : 
     438              :                 // check if coil off is less than load
     439            7 :                 PartLoadRatio = 0.0; // no coil capacity at full air flow
     440            7 :                 if (coilPlantLoc.loopNum > 0) {
     441            3 :                     state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
     442            3 :                     PlantUtilities::SetComponentFlowRate(
     443            3 :                         state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     444              :                 }
     445            7 :                 FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
     446            7 :                 if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput)) {
     447              :                     // otherwise iterate on load
     448            7 :                     if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
     449           21 :                         auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode, maxCoilFluidFlow, maxAirMassFlow](
     450              :                                      Real64 const PLR) {
     451           42 :                             return FanCoilUnits::CalcFanCoilWaterFlowResidual(state,
     452              :                                                                               PLR,
     453              :                                                                               SysIndex,
     454              :                                                                               FirstHVACIteration,
     455           21 :                                                                               SZVAVModel.ControlZoneNum,
     456              :                                                                               ZoneLoad,
     457           21 :                                                                               SZVAVModel.AirInNode,
     458              :                                                                               coilFluidInletNode,
     459              :                                                                               maxCoilFluidFlow,
     460           21 :                                                                               maxAirMassFlow);
     461            3 :                         };
     462            3 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     463            3 :                     } else {
     464           12 :                         auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
     465           24 :                             return FanCoilUnits::CalcFanCoilLoadResidual(
     466           12 :                                 state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
     467            4 :                         };
     468            4 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     469              :                     }
     470            7 :                     if (SolFlag < 0) {
     471            0 :                         MessagePrefix = "Step 3: ";
     472              :                     }
     473            7 :                 } else { // too much capacity at full air flow with coil off, operate coil and fan in unison
     474            0 :                     if (SZVAVModel.HCoilType_Num == FanCoilUnits::HCoil::Water || !HeatingLoad) {
     475            0 :                         auto f2 = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad, coilFluidInletNode](Real64 const PLR) {
     476            0 :                             return FanCoilUnits::CalcFanCoilAirAndWaterFlowResidual(state,
     477              :                                                                                     PLR,
     478              :                                                                                     SysIndex,
     479              :                                                                                     FirstHVACIteration,
     480            0 :                                                                                     SZVAVModel.ControlZoneNum,
     481              :                                                                                     ZoneLoad,
     482            0 :                                                                                     SZVAVModel.AirInNode,
     483              :                                                                                     coilFluidInletNode,
     484            0 :                                                                                     0.0);
     485            0 :                         };
     486            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f2, 0.0, 1.0);
     487            0 :                     } else {
     488            0 :                         auto f = [&state, SysIndex, FirstHVACIteration, &SZVAVModel, ZoneLoad](Real64 const PartLoadRatio) {
     489            0 :                             return FanCoilUnits::CalcFanCoilLoadResidual(
     490            0 :                                 state, SysIndex, FirstHVACIteration, SZVAVModel.ControlZoneNum, ZoneLoad, PartLoadRatio);
     491            0 :                         };
     492            0 :                         General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     493              :                     }
     494            0 :                     if (SolFlag < 0) {
     495            0 :                         MessagePrefix = "Step 3a: ";
     496              :                     }
     497              :                 }
     498              :             }
     499              : 
     500            9 :             if (coilPlantLoc.loopNum > 0)
     501            5 :                 PlantUtilities::SetComponentFlowRate(
     502            5 :                     state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     503              :         }
     504              : 
     505           11 :         if (SolFlag < 0) {
     506            0 :             if (SolFlag == -1) {
     507              :                 // get capacity for warning
     508            0 :                 FanCoilUnits::Calc4PipeFanCoil(state, SysIndex, SZVAVModel.ControlZoneNum, FirstHVACIteration, TempSensOutput, PartLoadRatio);
     509              : 
     510            0 :                 if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
     511              :                     15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
     512            0 :                     if (SZVAVModel.MaxIterIndex == 0) {
     513            0 :                         ShowWarningMessage(
     514            0 :                             state, format("{}Coil control failed to converge for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
     515            0 :                         ShowContinueError(state, "  Iteration limit exceeded in calculating system sensible part-load ratio.");
     516            0 :                         ShowContinueErrorTimeStamp(
     517              :                             state,
     518            0 :                             format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
     519              :                                    ZoneLoad,
     520              :                                    TempSensOutput));
     521              :                     }
     522            0 :                     ShowRecurringWarningErrorAtEnd(
     523              :                         state,
     524            0 :                         SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
     525              :                             "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
     526            0 :                         SZVAVModel.MaxIterIndex,
     527              :                         ZoneLoad,
     528              :                         ZoneLoad);
     529              :                 }
     530            0 :             } else if (SolFlag == -2) {
     531            0 :                 if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
     532            0 :                     ShowWarningMessage(state, format("{}Coil control failed for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
     533            0 :                     ShowContinueError(state, "  sensible part-load ratio determined to be outside the range of 0-1.");
     534            0 :                     ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
     535              :                 }
     536            0 :                 ShowRecurringWarningErrorAtEnd(state,
     537            0 :                                                SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
     538              :                                                    "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
     539            0 :                                                SZVAVModel.RegulaFalsiFailedIndex,
     540              :                                                ZoneLoad,
     541              :                                                ZoneLoad);
     542              :             }
     543              :         }
     544           11 :     }
     545              : 
     546           21 :     void calcSZVAVModel(EnergyPlusData &state,
     547              :                         UnitarySystems::UnitarySys &SZVAVModel,
     548              :                         int const SysIndex,
     549              :                         bool const FirstHVACIteration,
     550              :                         bool const CoolingLoad,
     551              :                         bool const HeatingLoad,
     552              :                         Real64 const ZoneLoad,
     553              :                         Real64 &OnOffAirFlowRatio,
     554              :                         bool const HXUnitOn,
     555              :                         int const AirLoopNum,
     556              :                         Real64 &PartLoadRatio,
     557              :                         HVAC::CompressorOp const CompressorONFlag)
     558              :     {
     559              : 
     560           21 :         UnitarySystems::UnitarySys &thisSys = state.dataUnitarySystems->unitarySys[SysIndex];
     561              : 
     562           21 :         int constexpr MaxIter(100); // maximum number of iterations
     563           21 :         int SolFlag(0);             // return flag from RegulaFalsi for sensible load
     564           21 :         std::string MessagePrefix;  // label for warning reporting
     565              : 
     566           21 :         Real64 boundaryLoadMet(0.0);
     567           21 :         Real64 minHumRat(0.0);
     568           21 :         Real64 outletTemp(0.0);
     569           21 :         bool coilActive(false);
     570           21 :         Real64 AirMassFlow(0.0);
     571              : 
     572           21 :         Real64 maxCoilFluidFlow(0.0);
     573           21 :         Real64 maxOutletTemp(0.0);
     574           21 :         Real64 minAirMassFlow(0.0);
     575           21 :         Real64 maxAirMassFlow(0.0);
     576           21 :         Real64 lowSpeedFanRatio(0.0);
     577           21 :         int coilFluidInletNode(0);
     578           21 :         int coilFluidOutletNode(0);
     579           21 :         PlantLocation coilPlantLoc{};
     580           21 :         int coilAirInletNode(0);
     581           21 :         int coilAirOutletNode(0);
     582           21 :         Real64 HeatCoilLoad(0.0);
     583           21 :         Real64 SupHeaterLoad(0.0);
     584              : 
     585              :         Real64 TempSensOutput; // iterative sensible capacity [W]
     586              :         Real64 TempLatOutput;  // iterative latent capacity [W]
     587              : 
     588              :         // set up mode specific variables to use in common function calls
     589           21 :         if (CoolingLoad) {
     590            7 :             maxCoilFluidFlow = SZVAVModel.MaxCoolCoilFluidFlow;
     591            7 :             maxOutletTemp = SZVAVModel.DesignMinOutletTemp;
     592            7 :             minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
     593            7 :             maxAirMassFlow = SZVAVModel.MaxCoolAirMassFlow;
     594            7 :             lowSpeedFanRatio = SZVAVModel.LowSpeedCoolFanRatio;
     595            7 :             coilFluidInletNode = SZVAVModel.CoolCoilFluidInletNode;
     596            7 :             coilFluidOutletNode = SZVAVModel.CoolCoilFluidOutletNodeNum;
     597            7 :             coilPlantLoc = SZVAVModel.CoolCoilPlantLoc;
     598            7 :             coilAirInletNode = SZVAVModel.CoolCoilInletNodeNum;
     599            7 :             coilAirOutletNode = SZVAVModel.CoolCoilOutletNodeNum;
     600           14 :         } else if (HeatingLoad) {
     601           14 :             maxCoilFluidFlow = SZVAVModel.MaxHeatCoilFluidFlow;
     602           14 :             maxOutletTemp = SZVAVModel.DesignMaxOutletTemp;
     603           14 :             minAirMassFlow = SZVAVModel.MaxNoCoolHeatAirMassFlow;
     604           14 :             maxAirMassFlow = SZVAVModel.MaxHeatAirMassFlow;
     605           14 :             lowSpeedFanRatio = SZVAVModel.LowSpeedHeatFanRatio;
     606           14 :             coilFluidInletNode = SZVAVModel.HeatCoilFluidInletNode;
     607           14 :             coilFluidOutletNode = SZVAVModel.HeatCoilFluidOutletNodeNum;
     608           14 :             coilPlantLoc = SZVAVModel.HeatCoilPlantLoc;
     609           14 :             coilAirInletNode = SZVAVModel.HeatCoilInletNodeNum;
     610           14 :             coilAirOutletNode = SZVAVModel.HeatCoilOutletNodeNum;
     611              :         } else { // should never get here, protect against uninitialized variables
     612            0 :             maxCoilFluidFlow = 0.0;
     613            0 :             maxOutletTemp = 0.0;
     614            0 :             minAirMassFlow = 0.0;
     615            0 :             maxAirMassFlow = 0.0;
     616            0 :             lowSpeedFanRatio = 0.0;
     617            0 :             coilFluidInletNode = 0;
     618            0 :             coilFluidOutletNode = 0;
     619            0 :             coilPlantLoc = {0, DataPlant::LoopSideLocation::Invalid, 0, 0};
     620            0 :             coilAirInletNode = 0;
     621            0 :             coilAirOutletNode = 0;
     622              :         }
     623              :         // set up RegulaFalsi variables
     624           21 :         int InletNode = SZVAVModel.AirInNode;
     625           21 :         Real64 InletTemp = state.dataLoopNodes->Node(InletNode).Temp;
     626           21 :         int OutletNode = SZVAVModel.AirOutNode;
     627           21 :         Real64 ZoneTemp = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).Temp;
     628           21 :         Real64 ZoneHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
     629              : 
     630              :         // model attempts to control air flow rate and coil capacity in specific operating regions:
     631              :         // Region 1 (R1) - minimum air flow rate at modulated coil capacity (up to min/max temperature limits)
     632              :         // Region 2 (R2) - modulated air flow rate and coil capacity (up to max air flow rate while maintaining min/max temperature limits)
     633              :         // Region 3 (R3) - maximum air flow rate and modulated/increased coil capacity (allow increased capacity at full air flow rate to meet
     634              :         // remaining load)
     635              :         //
     636              :         //                |    |                   |    |    ^            ^ = supply air temperature
     637              :         //                |    |                   |    | ^               * = supply air flow rate
     638              :         //                |    |                   |^^^^| <--- maximum supply air temperature
     639              :         //                |    |                ^  |    |
     640              :         //                |    |              ^    |    |
     641              :         //     ***********|    |            ^      |    |**************   <-- max unit air flow rate
     642              :         //                |*   |          ^        |   *|
     643              :         //                | *  |        ^          |  * |
     644              :         //                |  * |      ^            | *  |
     645              :         //                |   *|    ^              |*   |
     646              :         //                |    |*******************|    |                 <-- min unit air flow rate
     647              :         //          R3    | R2 | ^       R1        | R2 |    R3
     648              :         //   min SAT -->  |^^^^|                   |    |
     649              :         //               ^
     650              :         //             ^                  |
     651              :         //  (-) increasing cooling load < 0 > increasing heating load (+)
     652              :         //
     653              :         // Notes: SAT is allowed to decrease/increase once air flow rate is max'ed out otherwise load would not be met
     654              :         //        Load here is not the zone load, it's the load the system must meet to meet the Tstat set point (i.e., OA can alter required
     655              :         //        capacity) lowSpeedFanRatio = min/max unit air flow rate
     656              :         //
     657              :         // Step 1: calculate load at Region 1 lower (cooling) or upper (heating) boundary at minimum air flow rate
     658              :         //         - if load can be met, test maximum output (PLR = 1) before calling RootSolver
     659              :         //         - if maximum capacity is greater than load, solve for PLR
     660              :         // Step 2: calculate load at Region 3 lower (cooling) or upper (heating) boundary at maximum air flow rate
     661              :         //         - if load is less than boundary load, solve for air flow and PLR that meet the load
     662              :         //         - ELSE
     663              :         // Step 3: solve for Region 3 PLR
     664              :         //       DONE
     665              :         //
     666              : 
     667              :         // Step 1: Determine boundary for region 1
     668              :         // calculate sensible load based on minimum air flow rate and specified supply air temperature limit
     669           21 :         if (SZVAVModel.ATMixerExists) {
     670            0 :             if (SZVAVModel.ATMixerType == HVAC::MixerType::SupplySide) {
     671              :                 // Air terminal supply side mixer
     672            0 :                 boundaryLoadMet =
     673            0 :                     minAirMassFlow * (Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(SZVAVModel.ATMixerOutNode).Temp, ZoneHumRat) -
     674            0 :                                       Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
     675              :             } else {
     676              :                 // Air terminal inlet side mixer
     677            0 :                 boundaryLoadMet =
     678            0 :                     minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, ZoneHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, ZoneHumRat));
     679              :             }
     680              :         } else {
     681           21 :             minHumRat = min(state.dataLoopNodes->Node(InletNode).HumRat, state.dataLoopNodes->Node(OutletNode).HumRat);
     682           21 :             boundaryLoadMet =
     683           21 :                 minAirMassFlow * (Psychrometrics::PsyHFnTdbW(maxOutletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(InletTemp, minHumRat));
     684              :         }
     685              : 
     686           21 :         if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 1 of figure
     687              :             // Step 1: set min air flow and full coil capacity
     688           10 :             PartLoadRatio = 1.0; // full coil capacity
     689           10 :             SZVAVModel.FanPartLoadRatio =
     690              :                 0.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
     691           10 :             state.dataLoopNodes->Node(InletNode).MassFlowRate = minAirMassFlow;
     692              :             // set max water flow rate and check to see if plant limits flow
     693           10 :             if (coilPlantLoc.loopNum > 0)
     694            4 :                 PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     695              : 
     696           10 :             if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
     697              :                 // set the water flow ratio so water coil gets proper flow
     698            3 :                 if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
     699            3 :                 thisSys.calcUnitarySystemToLoad(state,
     700              :                                                 AirLoopNum,
     701              :                                                 FirstHVACIteration,
     702              :                                                 PartLoadRatio,
     703              :                                                 0.0,
     704              :                                                 OnOffAirFlowRatio,
     705              :                                                 TempSensOutput,
     706              :                                                 TempLatOutput,
     707              :                                                 HXUnitOn,
     708              :                                                 HeatCoilLoad,
     709              :                                                 SupHeaterLoad,
     710              :                                                 CompressorONFlag);
     711              :             } else {
     712            7 :                 if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
     713            7 :                 thisSys.calcUnitarySystemToLoad(state,
     714              :                                                 AirLoopNum,
     715              :                                                 FirstHVACIteration,
     716              :                                                 0.0,
     717              :                                                 PartLoadRatio,
     718              :                                                 OnOffAirFlowRatio,
     719              :                                                 TempSensOutput,
     720              :                                                 TempLatOutput,
     721              :                                                 HXUnitOn,
     722              :                                                 ZoneLoad,
     723              :                                                 SupHeaterLoad,
     724              :                                                 CompressorONFlag);
     725              :             }
     726           10 :             coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
     727              : 
     728           10 :             if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
     729            2 :                 if (coilPlantLoc.loopNum > 0) {
     730            1 :                     state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
     731            1 :                     PlantUtilities::SetComponentFlowRate(
     732            1 :                         state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     733              :                 }
     734            2 :                 return;
     735              :             }
     736              : 
     737            8 :             if ((CoolingLoad && TempSensOutput < ZoneLoad) || (HeatingLoad && TempSensOutput > ZoneLoad)) { // low speed fan can meet load
     738           38 :                 auto f = [&state,
     739              :                           SysIndex,
     740              :                           FirstHVACIteration,
     741              :                           ZoneLoad,
     742              :                           &SZVAVModel,
     743            8 :                           OnOffAirFlowRatio,
     744              :                           AirLoopNum,
     745              :                           coilFluidInletNode,
     746              :                           lowSpeedFanRatio,
     747              :                           maxCoilFluidFlow,
     748              :                           minAirMassFlow,
     749              :                           maxAirMassFlow,
     750              :                           CoolingLoad](Real64 const PartLoadRatio) {
     751           76 :                     return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
     752              :                                                                                           PartLoadRatio, // coil part load ratio
     753              :                                                                                           SysIndex,
     754              :                                                                                           FirstHVACIteration,
     755              :                                                                                           ZoneLoad,
     756           38 :                                                                                           SZVAVModel.AirInNode,
     757              :                                                                                           OnOffAirFlowRatio,
     758              :                                                                                           AirLoopNum,
     759              :                                                                                           coilFluidInletNode,
     760              :                                                                                           maxCoilFluidFlow,
     761              :                                                                                           lowSpeedFanRatio,
     762              :                                                                                           minAirMassFlow,
     763              :                                                                                           0.0,
     764              :                                                                                           maxAirMassFlow,
     765              :                                                                                           CoolingLoad,
     766           38 :                                                                                           1.0);
     767            8 :                 };
     768            8 :                 General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     769            8 :                 if (SolFlag < 0) {
     770            0 :                     MessagePrefix = "Step 1: ";
     771              :                 }
     772              : 
     773            8 :                 if (coilPlantLoc.loopNum > 0)
     774            3 :                     PlantUtilities::SetComponentFlowRate(
     775            3 :                         state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     776              :             }
     777              : 
     778            8 :         } else {
     779              : 
     780              :             // Step 2: Load is greater than allowed in region 1, determine boundary load for region 3
     781              :             // only difference in this calculation is using maxAirMassFlow instead of minAirMassFlow, just use the ratio to adjust previous
     782              :             // calculation
     783           11 :             boundaryLoadMet *= maxAirMassFlow / minAirMassFlow;
     784              : 
     785           11 :             if ((CoolingLoad && boundaryLoadMet < ZoneLoad) || (HeatingLoad && boundaryLoadMet > ZoneLoad)) { // in Region 2 of figure
     786              : 
     787            6 :                 outletTemp = state.dataLoopNodes->Node(OutletNode).Temp;
     788            6 :                 minHumRat = state.dataLoopNodes->Node(SZVAVModel.NodeNumOfControlledZone).HumRat;
     789            6 :                 if (outletTemp < ZoneTemp) minHumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
     790            6 :                 outletTemp = maxOutletTemp;
     791            6 :                 AirMassFlow = min(maxAirMassFlow,
     792            6 :                                   (ZoneLoad / (Psychrometrics::PsyHFnTdbW(outletTemp, minHumRat) - Psychrometrics::PsyHFnTdbW(ZoneTemp, minHumRat))));
     793            6 :                 AirMassFlow = max(minAirMassFlow, AirMassFlow);
     794            6 :                 SZVAVModel.FanPartLoadRatio = ((AirMassFlow - (maxAirMassFlow * lowSpeedFanRatio)) / ((1.0 - lowSpeedFanRatio) * maxAirMassFlow));
     795              : 
     796           33 :                 auto f = [&state,
     797              :                           SysIndex,
     798              :                           FirstHVACIteration,
     799              :                           ZoneLoad,
     800              :                           &SZVAVModel,
     801            6 :                           OnOffAirFlowRatio,
     802              :                           AirLoopNum,
     803              :                           coilFluidInletNode,
     804              :                           lowSpeedFanRatio,
     805              :                           AirMassFlow,
     806              :                           maxAirMassFlow,
     807              :                           CoolingLoad,
     808              :                           maxCoilFluidFlow](Real64 const PartLoadRatio) {
     809           66 :                     return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
     810              :                                                                                           PartLoadRatio, // coil part load ratio
     811              :                                                                                           SysIndex,
     812              :                                                                                           FirstHVACIteration,
     813              :                                                                                           ZoneLoad,
     814           33 :                                                                                           SZVAVModel.AirInNode,
     815              :                                                                                           OnOffAirFlowRatio,
     816              :                                                                                           AirLoopNum,
     817              :                                                                                           coilFluidInletNode,
     818              :                                                                                           maxCoilFluidFlow,
     819              :                                                                                           lowSpeedFanRatio,
     820              :                                                                                           AirMassFlow,
     821              :                                                                                           0.0,
     822              :                                                                                           maxAirMassFlow,
     823              :                                                                                           CoolingLoad,
     824           33 :                                                                                           1.0);
     825            6 :                 };
     826            6 :                 General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     827            6 :                 if (SolFlag < 0) {
     828            0 :                     MessagePrefix = "Step 2: ";
     829              :                 }
     830              : 
     831            6 :             } else { // in region 3 of figure
     832              : 
     833            5 :                 PartLoadRatio = 1.0; // full coil capacity
     834            5 :                 SZVAVModel.FanPartLoadRatio =
     835              :                     1.0; // minimum fan PLR, air flow = ( fanPartLoadRatio * maxAirMassFlow ) + ( ( 1.0 - fanPartLoadRatio ) * minAirMassFlow )
     836            5 :                 state.dataLoopNodes->Node(InletNode).MassFlowRate = maxAirMassFlow;
     837              :                 // set max water flow rate and check to see if plant limits flow
     838            5 :                 if (coilPlantLoc.loopNum > 0)
     839            2 :                     PlantUtilities::SetComponentFlowRate(state, maxCoilFluidFlow, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     840              : 
     841            5 :                 if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
     842              :                     // set the water flow ratio so water coil gets proper flow
     843            2 :                     if (SZVAVModel.MaxCoolCoilFluidFlow > 0.0) SZVAVModel.CoolCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxCoolCoilFluidFlow;
     844            2 :                     thisSys.calcUnitarySystemToLoad(state,
     845              :                                                     AirLoopNum,
     846              :                                                     FirstHVACIteration,
     847              :                                                     PartLoadRatio,
     848              :                                                     0.0,
     849              :                                                     OnOffAirFlowRatio,
     850              :                                                     TempSensOutput,
     851              :                                                     TempLatOutput,
     852              :                                                     HXUnitOn,
     853              :                                                     HeatCoilLoad,
     854              :                                                     SupHeaterLoad,
     855              :                                                     CompressorONFlag);
     856              :                 } else {
     857            3 :                     if (SZVAVModel.MaxHeatCoilFluidFlow > 0.0) SZVAVModel.HeatCoilWaterFlowRatio = maxCoilFluidFlow / SZVAVModel.MaxHeatCoilFluidFlow;
     858            3 :                     thisSys.calcUnitarySystemToLoad(state,
     859              :                                                     AirLoopNum,
     860              :                                                     FirstHVACIteration,
     861              :                                                     0.0,
     862              :                                                     PartLoadRatio,
     863              :                                                     OnOffAirFlowRatio,
     864              :                                                     TempSensOutput,
     865              :                                                     TempLatOutput,
     866              :                                                     HXUnitOn,
     867              :                                                     ZoneLoad,
     868              :                                                     SupHeaterLoad,
     869              :                                                     CompressorONFlag);
     870              :                 }
     871            5 :                 coilActive = state.dataLoopNodes->Node(coilAirInletNode).Temp - state.dataLoopNodes->Node(coilAirOutletNode).Temp;
     872            5 :                 if (!coilActive) { // if the coil is schedule off or the plant cannot provide water
     873            0 :                     if (coilPlantLoc.loopNum > 0) {
     874            0 :                         state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate = 0.0;
     875            0 :                         PlantUtilities::SetComponentFlowRate(
     876            0 :                             state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     877              :                     }
     878            1 :                     return;
     879              :                 }
     880              : 
     881            5 :                 if ((CoolingLoad && ZoneLoad < TempSensOutput) || (HeatingLoad && ZoneLoad > TempSensOutput))
     882            1 :                     return; // system cannot meet load, leave at max capacity
     883              : 
     884              :                 // otherwise iterate on load
     885           24 :                 auto f = [&state,
     886              :                           SysIndex,
     887              :                           FirstHVACIteration,
     888              :                           ZoneLoad,
     889              :                           &SZVAVModel,
     890            4 :                           OnOffAirFlowRatio,
     891              :                           AirLoopNum,
     892              :                           coilFluidInletNode,
     893              :                           lowSpeedFanRatio,
     894              :                           maxCoilFluidFlow,
     895              :                           maxAirMassFlow,
     896              :                           CoolingLoad](Real64 const PartLoadRatio) {
     897           48 :                     return UnitarySystems::UnitarySys::calcUnitarySystemWaterFlowResidual(state,
     898              :                                                                                           PartLoadRatio, // coil part load ratio
     899              :                                                                                           SysIndex,
     900              :                                                                                           FirstHVACIteration,
     901              :                                                                                           ZoneLoad,
     902           24 :                                                                                           SZVAVModel.AirInNode,
     903              :                                                                                           OnOffAirFlowRatio,
     904              :                                                                                           AirLoopNum,
     905              :                                                                                           coilFluidInletNode,
     906              :                                                                                           maxCoilFluidFlow,
     907              :                                                                                           lowSpeedFanRatio,
     908              :                                                                                           maxAirMassFlow,
     909              :                                                                                           0.0,
     910              :                                                                                           maxAirMassFlow,
     911              :                                                                                           CoolingLoad,
     912           24 :                                                                                           1.0);
     913            4 :                 };
     914            4 :                 General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, f, 0.0, 1.0);
     915              :                 //                Par[12] = maxAirMassFlow; // operating air flow rate, minAirMassFlow indicates low speed air flow rate,
     916              :                 //                maxAirMassFlow indicates full
     917              :                 //                                          // air flow
     918              :                 //                Par[13] = 0.0;            // SA Temp target, 0 means iterate on load and not SA temperature
     919              :                 //                General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadRatio, thisSys.calcUnitarySystemWaterFlowResidual,
     920              :                 //                0.0, 1.0, Par);
     921            4 :                 if (SolFlag < 0) {
     922            0 :                     MessagePrefix = "Step 3: ";
     923              :                 }
     924              :             }
     925              : 
     926           10 :             if (coilPlantLoc.loopNum > 0)
     927            4 :                 PlantUtilities::SetComponentFlowRate(
     928            4 :                     state, state.dataLoopNodes->Node(coilFluidInletNode).MassFlowRate, coilFluidInletNode, coilFluidOutletNode, coilPlantLoc);
     929              :         }
     930              : 
     931           18 :         if (SolFlag < 0) {
     932            0 :             if (SolFlag == -1) {
     933              :                 // get capacity for warning
     934            0 :                 if (CoolingLoad) { // Function CalcUnitarySystemToLoad, 4th and 5th arguments are CoolPLR and HeatPLR
     935            0 :                     thisSys.calcUnitarySystemToLoad(state,
     936              :                                                     AirLoopNum,
     937              :                                                     FirstHVACIteration,
     938              :                                                     PartLoadRatio,
     939              :                                                     0.0,
     940              :                                                     OnOffAirFlowRatio,
     941              :                                                     TempSensOutput,
     942              :                                                     TempLatOutput,
     943              :                                                     HXUnitOn,
     944              :                                                     HeatCoilLoad,
     945              :                                                     SupHeaterLoad,
     946              :                                                     CompressorONFlag);
     947              :                 } else {
     948            0 :                     thisSys.calcUnitarySystemToLoad(state,
     949              :                                                     AirLoopNum,
     950              :                                                     FirstHVACIteration,
     951              :                                                     0.0,
     952              :                                                     PartLoadRatio,
     953              :                                                     OnOffAirFlowRatio,
     954              :                                                     TempSensOutput,
     955              :                                                     TempLatOutput,
     956              :                                                     HXUnitOn,
     957              :                                                     ZoneLoad,
     958              :                                                     SupHeaterLoad,
     959              :                                                     CompressorONFlag);
     960              :                 }
     961              : 
     962            0 :                 if (std::abs(TempSensOutput - ZoneLoad) * SZVAVModel.ControlZoneMassFlowFrac >
     963              :                     15.0) { // water coil can provide same output at varying water PLR (model discontinuity?)
     964            0 :                     if (SZVAVModel.MaxIterIndex == 0) {
     965            0 :                         ShowWarningMessage(
     966            0 :                             state, format("{}Coil control failed to converge for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
     967            0 :                         ShowContinueError(state, "  Iteration limit exceeded in calculating system sensible part-load ratio.");
     968            0 :                         ShowContinueErrorTimeStamp(
     969              :                             state,
     970            0 :                             format("Sensible load to be met = {:.2T} (watts), sensible output = {:.2T} (watts), and the simulation continues.",
     971              :                                    ZoneLoad,
     972              :                                    TempSensOutput));
     973              :                     }
     974            0 :                     ShowRecurringWarningErrorAtEnd(
     975              :                         state,
     976            0 :                         SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
     977              :                             "\" - Iteration limit exceeded in calculating sensible part-load ratio error continues. Sensible load statistics:",
     978            0 :                         SZVAVModel.MaxIterIndex,
     979              :                         ZoneLoad,
     980              :                         ZoneLoad);
     981              :                 }
     982            0 :             } else if (SolFlag == -2) {
     983            0 :                 if (SZVAVModel.RegulaFalsiFailedIndex == 0) {
     984            0 :                     ShowWarningMessage(state, format("{}Coil control failed for {}:{}", MessagePrefix, SZVAVModel.UnitType, SZVAVModel.Name));
     985            0 :                     ShowContinueError(state, "  sensible part-load ratio determined to be outside the range of 0-1.");
     986            0 :                     ShowContinueErrorTimeStamp(state, format("Sensible load to be met = {:.2T} (watts), and the simulation continues.", ZoneLoad));
     987              :                 }
     988            0 :                 ShowRecurringWarningErrorAtEnd(state,
     989            0 :                                                SZVAVModel.UnitType + " \"" + SZVAVModel.Name +
     990              :                                                    "\" - sensible part-load ratio out of range error continues. Sensible load statistics:",
     991            0 :                                                SZVAVModel.RegulaFalsiFailedIndex,
     992              :                                                ZoneLoad,
     993              :                                                ZoneLoad);
     994              :             }
     995              :         }
     996           21 :     }
     997              : 
     998              : } // namespace SZVAVModel
     999              : 
    1000              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1