LCOV - code coverage report
Current view: top level - EnergyPlus - SZVAVModel.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 35.3 % 450 159
Test Date: 2025-06-02 07:23:51 Functions: 35.3 % 17 6

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

Generated by: LCOV version 2.0-1