LCOV - code coverage report
Current view: top level - EnergyPlus - SZVAVModel.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 150 430 34.9 %
Date: 2024-08-24 18:31:18 Functions: 6 17 35.3 %

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

Generated by: LCOV version 1.14