LCOV - code coverage report
Current view: top level - EnergyPlus - SZVAVModel.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 141 398 35.4 %
Date: 2023-01-17 19:17:23 Functions: 8 19 42.1 %

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

Generated by: LCOV version 1.13