LCOV - code coverage report
Current view: top level - EnergyPlus/Coils - CoilCoolingDXCurveFitSpeed.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 73.7 % 452 333
Test Date: 2025-05-22 16:09:37 Functions: 85.7 % 7 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              : #include <utility>
      49              : 
      50              : #include <EnergyPlus/Autosizing/CoolingAirFlowSizing.hh>
      51              : #include <EnergyPlus/Autosizing/CoolingCapacitySizing.hh>
      52              : #include <EnergyPlus/Autosizing/CoolingSHRSizing.hh>
      53              : #include <EnergyPlus/Coils/CoilCoolingDXCurveFitSpeed.hh>
      54              : #include <EnergyPlus/CurveManager.hh>
      55              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      56              : #include <EnergyPlus/DataEnvironment.hh>
      57              : #include <EnergyPlus/DataHVACGlobals.hh>
      58              : #include <EnergyPlus/DataIPShortCuts.hh>
      59              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      60              : #include <EnergyPlus/DataSizing.hh>
      61              : #include <EnergyPlus/General.hh>
      62              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      63              : #include <EnergyPlus/Psychrometrics.hh>
      64              : 
      65              : using namespace EnergyPlus;
      66              : 
      67           58 : void CoilCoolingDXCurveFitSpeed::instantiateFromInputSpec(EnergyPlus::EnergyPlusData &state,
      68              :                                                           const CoilCoolingDXCurveFitSpeedInputSpecification &input_data)
      69              : {
      70           58 :     bool errorsFound(false);
      71              :     static constexpr std::string_view routineName = "CoilCoolingDXCurveFitSpeed::instantiateFromInputSpec: ";
      72              :     static constexpr std::string_view fieldName = "Part Load Fraction Correlation Curve Name";
      73           58 :     this->original_input_specs = input_data;
      74           58 :     this->name = input_data.name;
      75           58 :     this->active_fraction_of_face_coil_area = input_data.active_fraction_of_coil_face_area;
      76           58 :     if (this->active_fraction_of_face_coil_area < 1.0) this->adjustForFaceArea = true;
      77           58 :     this->rated_evap_fan_power_per_volume_flow_rate = input_data.rated_evaporator_fan_power_per_volume_flow_rate;
      78           58 :     this->rated_evap_fan_power_per_volume_flow_rate_2023 = input_data.rated_evaporator_fan_power_per_volume_flow_rate_2023;
      79           58 :     this->evap_condenser_pump_power_fraction = input_data.rated_evaporative_condenser_pump_power_fraction;
      80           58 :     this->evap_condenser_effectiveness = input_data.evaporative_condenser_effectiveness;
      81           58 :     this->ratedWasteHeatFractionOfPowerInput = input_data.rated_waste_heat_fraction_of_power_input;
      82           58 :     this->ratedCOP = input_data.gross_rated_cooling_COP;
      83          348 :     errorsFound |= this->processCurve(state,
      84           58 :                                       input_data.total_cooling_capacity_function_of_temperature_curve_name,
      85           58 :                                       this->indexCapFT,
      86              :                                       {1, 2}, // MULTIPLECURVEDIMS
      87              :                                       routineName,
      88              :                                       "Total Cooling Capacity Function of Temperature Curve Name",
      89              :                                       RatedInletWetBulbTemp,
      90           58 :                                       RatedOutdoorAirTemp);
      91              : 
      92          348 :     errorsFound |= this->processCurve(state,
      93           58 :                                       input_data.total_cooling_capacity_function_of_air_flow_fraction_curve_name,
      94           58 :                                       this->indexCapFFF,
      95              :                                       {1},
      96              :                                       routineName,
      97              :                                       "Total Cooling Capacity Function of Air Flow Fraction Curve Name",
      98              :                                       1.0);
      99              : 
     100          348 :     errorsFound |= this->processCurve(state,
     101           58 :                                       input_data.energy_input_ratio_function_of_temperature_curve_name,
     102           58 :                                       this->indexEIRFT,
     103              :                                       {1, 2}, // MULTIPLECURVEDIMS
     104              :                                       routineName,
     105              :                                       "Energy Input Ratio Function of Temperature Curve Name",
     106              :                                       RatedInletWetBulbTemp,
     107           58 :                                       RatedOutdoorAirTemp);
     108              : 
     109          348 :     errorsFound |= this->processCurve(state,
     110           58 :                                       input_data.energy_input_ratio_function_of_air_flow_fraction_curve_name,
     111           58 :                                       this->indexEIRFFF,
     112              :                                       {1},
     113              :                                       routineName,
     114              :                                       "Energy Input Ratio Function of Air Flow Fraction Curve Name",
     115              :                                       1.0);
     116              : 
     117          348 :     errorsFound |= this->processCurve(state,
     118           58 :                                       input_data.sensible_heat_ratio_modifier_function_of_temperature_curve_name,
     119           58 :                                       this->indexSHRFT,
     120              :                                       {2}, // Only allow bivariate functions since curve inputs are different from other f(Temp) functions
     121              :                                       routineName,
     122              :                                       "Sensible Heat Ratio Modifier Function of Temperature Curve Name",
     123              :                                       RatedInletWetBulbTemp,
     124           58 :                                       RatedOutdoorAirTemp);
     125              : 
     126          348 :     errorsFound |= this->processCurve(state,
     127           58 :                                       input_data.sensible_heat_ratio_modifier_function_of_flow_fraction_curve_name,
     128           58 :                                       this->indexSHRFFF,
     129              :                                       {1},
     130              :                                       routineName,
     131              :                                       "Sensible Heat Ratio Modifier Function of Air Flow Fraction Curve Name",
     132              :                                       1.0);
     133              : 
     134          348 :     errorsFound |= this->processCurve(state,
     135           58 :                                       input_data.waste_heat_function_of_temperature_curve_name,
     136           58 :                                       this->indexWHFT,
     137              :                                       {2},
     138              :                                       routineName,
     139              :                                       "Waste Heat Modifier Function of Temperature Curve Name",
     140              :                                       RatedOutdoorAirTemp,
     141           58 :                                       RatedInletAirTemp);
     142              : 
     143           58 :     if (!errorsFound && !input_data.waste_heat_function_of_temperature_curve_name.empty()) {
     144           26 :         Real64 CurveVal = Curve::CurveValue(state, this->indexWHFT, RatedOutdoorAirTemp, RatedInletAirTemp);
     145           26 :         if (CurveVal > 1.10 || CurveVal < 0.90) {
     146            0 :             ShowWarningError(state, std::string{routineName} + this->object_name + "=\"" + this->name + "\", curve values");
     147            0 :             ShowContinueError(state,
     148            0 :                               "Waste Heat Modifier Function of Temperature Curve Name = " + input_data.waste_heat_function_of_temperature_curve_name);
     149            0 :             ShowContinueError(
     150              :                 state, "...Waste Heat Modifier Function of Temperature Curve Name output is not equal to 1.0 (+ or - 10%) at rated conditions.");
     151            0 :             ShowContinueError(state, format("...Curve output at rated conditions = {:.3T}", CurveVal));
     152              :         }
     153              :     }
     154              : 
     155          348 :     errorsFound |= this->processCurve(state,
     156           58 :                                       input_data.part_load_fraction_correlation_curve_name,
     157           58 :                                       this->indexPLRFPLF,
     158              :                                       {1},
     159              :                                       routineName,
     160              :                                       "Part Load Fraction Correlation Curve Name",
     161              :                                       1.0);
     162              : 
     163           58 :     if (this->indexPLRFPLF > 0 && !errorsFound) {
     164              :         //     Test PLF curve minimum and maximum. Cap if less than 0.7 or greater than 1.0.
     165           50 :         Real64 MinCurveVal = 999.0;
     166           50 :         Real64 MaxCurveVal = -999.0;
     167           50 :         Real64 CurveInput = 0.0;
     168           50 :         Real64 MinCurvePLR = 0.0, MaxCurvePLR = 0.0;
     169         5050 :         while (CurveInput <= 1.0) {
     170         5000 :             Real64 CurveVal = Curve::CurveValue(state, this->indexPLRFPLF, CurveInput);
     171         5000 :             if (CurveVal < MinCurveVal) {
     172           50 :                 MinCurveVal = CurveVal;
     173           50 :                 MinCurvePLR = CurveInput;
     174              :             }
     175         5000 :             if (CurveVal > MaxCurveVal) {
     176         2905 :                 MaxCurveVal = CurveVal;
     177         2905 :                 MaxCurvePLR = CurveInput;
     178              :             }
     179         5000 :             CurveInput += 0.01;
     180              :         }
     181           50 :         if (MinCurveVal < 0.7) {
     182            0 :             ShowWarningError(state, format("{}{}=\"{}\", invalid", routineName, this->object_name, this->name));
     183            0 :             ShowContinueError(state, format("...{}=\"{}\" has out of range value.", fieldName, input_data.part_load_fraction_correlation_curve_name));
     184            0 :             ShowContinueError(state, format("...Curve minimum must be >= 0.7, curve min at PLR = {:.2T} is {:.3T}", MinCurvePLR, MinCurveVal));
     185            0 :             ShowContinueError(state, "...Setting curve minimum to 0.7 and simulation continues.");
     186            0 :             Curve::SetCurveOutputMinValue(state, this->indexPLRFPLF, errorsFound, 0.7);
     187              :         }
     188              : 
     189           50 :         if (MaxCurveVal > 1.0) {
     190            0 :             ShowWarningError(state, format("{}{}=\"{}\", invalid", routineName, this->object_name, this->name));
     191            0 :             ShowContinueError(state, format("...{}=\"{}\" has out of range value.", fieldName, input_data.part_load_fraction_correlation_curve_name));
     192            0 :             ShowContinueError(state, format("...Curve maximum must be <= 1.0, curve max at PLR = {:.2T} is {:.3T}", MaxCurvePLR, MaxCurveVal));
     193            0 :             ShowContinueError(state, "...Setting curve maximum to 1.0 and simulation continues.");
     194            0 :             Curve::SetCurveOutputMaxValue(state, this->indexPLRFPLF, errorsFound, 1.0);
     195              :         }
     196              :     }
     197              : 
     198           58 :     if (errorsFound) {
     199            0 :         ShowFatalError(
     200            0 :             state, std::string{routineName} + "Errors found in getting " + this->object_name + " input. Preceding condition(s) causes termination.");
     201              :     }
     202           58 : }
     203              : 
     204          464 : bool CoilCoolingDXCurveFitSpeed::processCurve(EnergyPlus::EnergyPlusData &state,
     205              :                                               const std::string &curveName,
     206              :                                               int &curveIndex,
     207              :                                               std::vector<int> validDims,
     208              :                                               std::string_view const routineName,
     209              :                                               const std::string &fieldName,
     210              :                                               Real64 const Var1,                      // required 1st independent variable
     211              :                                               ObjexxFCL::Optional<Real64 const> Var2) // 2nd independent variable
     212              : {
     213          464 :     if (curveName.empty()) {
     214          154 :         return false;
     215              :     } else {
     216          310 :         curveIndex = Curve::GetCurveIndex(state, curveName);
     217          310 :         if (curveIndex == 0) {
     218            0 :             ShowSevereError(state, std::string{routineName} + this->object_name + "=\"" + this->name + "\", invalid");
     219            0 :             ShowContinueError(state, "...not found " + fieldName + "=\"" + curveName + "\".");
     220            0 :             return true;
     221              :         } else {
     222              :             // Verify Curve Object dimensions
     223          620 :             bool errorFound = Curve::CheckCurveDims(state,
     224              :                                                     curveIndex,           // Curve index
     225          310 :                                                     std::move(validDims), // Valid dimensions
     226              :                                                     routineName,          // Routine name
     227              :                                                     this->object_name,    // Object Type
     228              :                                                     this->name,           // Object Name
     229              :                                                     fieldName);           // Field Name
     230          310 :             if (!errorFound) {
     231          310 :                 if (Var2.present()) {
     232          286 :                     Curve::checkCurveIsNormalizedToOne(
     233          572 :                         state, std::string{routineName} + this->object_name, this->name, curveIndex, fieldName, curveName, Var1, Var2);
     234              :                 } else {
     235          167 :                     Curve::checkCurveIsNormalizedToOne(
     236          668 :                         state, std::string{routineName} + this->object_name, this->name, curveIndex, fieldName, curveName, Var1);
     237              :                 }
     238              :             }
     239          310 :             return errorFound;
     240              :         }
     241              :     }
     242              : }
     243              : 
     244           58 : CoilCoolingDXCurveFitSpeed::CoilCoolingDXCurveFitSpeed(EnergyPlus::EnergyPlusData &state,
     245           58 :                                                        const std::string &name_to_find)
     246              :     : // model inputs
     247           58 :       indexCapFT(0), indexCapFFF(0), indexEIRFT(0), indexEIRFFF(0), indexPLRFPLF(0), indexWHFT(0), indexSHRFT(0), indexSHRFFF(0),
     248              : 
     249              :       // speed class inputs
     250           58 :       RatedAirMassFlowRate(0.0),     // rated air mass flow rate at speed {kg/s}
     251           58 :       RatedCondAirMassFlowRate(0.0), // condenser air mass flow rate at speed {kg/s}
     252           58 :       grossRatedSHR(0.0),            // rated sensible heat ratio at speed
     253           58 :       RatedCBF(0.0),                 // rated coil bypass factor at speed
     254           58 :       RatedEIR(0.0),                 // rated energy input ratio at speed {W/W}
     255           58 :       ratedCOP(0.0), rated_total_capacity(0.0), rated_evap_fan_power_per_volume_flow_rate(0.0),
     256           58 :       ratedWasteHeatFractionOfPowerInput(0.0), // rated waste heat fraction of power input
     257           58 :       evap_condenser_pump_power_fraction(0.0), evap_condenser_effectiveness(0.0),
     258              : 
     259           58 :       parentModeRatedGrossTotalCap(0.0), parentModeRatedEvapAirFlowRate(0.0), parentModeRatedCondAirFlowRate(0.0), parentOperatingMode(0),
     260              : 
     261           58 :       ambPressure(0.0), // outdoor pressure {Pa}
     262           58 :       PLR(0.0),         // coil operating part load ratio
     263           58 :       AirFF(0.0),       // ratio of air mass flow rate to rated air mass flow rate
     264              :       // RatedTotCap( 0.0 ), // rated total capacity at speed {W}
     265              : 
     266           58 :       fullLoadPower(0.0),     // full load power at speed {W}
     267           58 :       fullLoadWasteHeat(0.0), // full load waste heat at speed {W}
     268           58 :       RTF(0.0),               // coil runtime fraction at speed
     269           58 :       AirMassFlow(0.0),       // coil inlet air mass flow rate {kg/s}
     270              : 
     271              :       // other data members
     272           58 :       evap_air_flow_rate(0.0), condenser_air_flow_rate(0.0), active_fraction_of_face_coil_area(0.0),
     273           58 :       ratedLatentCapacity(0.0), // Latent capacity at rated conditions {W}
     274              : 
     275              :       // rating data
     276           58 :       RatedInletAirTemp(26.6667),       // 26.6667C or 80F
     277           58 :       RatedInletWetBulbTemp(19.4444),   // 19.44 or 67F
     278           58 :       RatedInletAirHumRat(0.0111847),   // Humidity ratio corresponding to 80F dry bulb/67F wet bulb
     279           58 :       RatedOutdoorAirTemp(35.0),        // 35 C or 95F
     280          174 :       DryCoilOutletHumRatioMin(0.00001) // dry coil outlet minimum hum ratio kgH2O/kgdry air
     281              : 
     282              : {
     283           58 :     int numSpeeds = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CoilCoolingDXCurveFitSpeed::object_name);
     284              :     if (numSpeeds <= 0) {
     285              :         // error
     286              :     }
     287           58 :     bool found_it = false;
     288          121 :     for (int speedNum = 1; speedNum <= numSpeeds; ++speedNum) {
     289              :         int NumAlphas;  // Number of Alphas for each GetObjectItem call
     290              :         int NumNumbers; // Number of Numbers for each GetObjectItem call
     291              :         int IOStatus;
     292          242 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     293              :                                                                  CoilCoolingDXCurveFitSpeed::object_name,
     294              :                                                                  speedNum,
     295          121 :                                                                  state.dataIPShortCut->cAlphaArgs,
     296              :                                                                  NumAlphas,
     297          121 :                                                                  state.dataIPShortCut->rNumericArgs,
     298              :                                                                  NumNumbers,
     299              :                                                                  IOStatus);
     300          121 :         if (!Util::SameString(name_to_find, state.dataIPShortCut->cAlphaArgs(1))) {
     301           63 :             continue;
     302              :         }
     303           58 :         found_it = true;
     304              : 
     305           58 :         CoilCoolingDXCurveFitSpeedInputSpecification input_specs;
     306              : 
     307           58 :         input_specs.name = state.dataIPShortCut->cAlphaArgs(1);
     308           58 :         input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal = state.dataIPShortCut->rNumericArgs(1);
     309           58 :         input_specs.evaporator_air_flow_fraction = state.dataIPShortCut->rNumericArgs(2);
     310           58 :         input_specs.condenser_air_flow_fraction = state.dataIPShortCut->rNumericArgs(3);
     311           58 :         input_specs.gross_rated_sensible_heat_ratio = state.dataIPShortCut->rNumericArgs(4);
     312           58 :         input_specs.gross_rated_cooling_COP = state.dataIPShortCut->rNumericArgs(5);
     313           58 :         input_specs.active_fraction_of_coil_face_area = state.dataIPShortCut->rNumericArgs(6);
     314           58 :         input_specs.rated_evaporator_fan_power_per_volume_flow_rate = state.dataIPShortCut->rNumericArgs(7);
     315           58 :         input_specs.rated_evaporator_fan_power_per_volume_flow_rate_2023 = state.dataIPShortCut->rNumericArgs(8);
     316           58 :         input_specs.rated_evaporative_condenser_pump_power_fraction = state.dataIPShortCut->rNumericArgs(9);
     317           58 :         input_specs.evaporative_condenser_effectiveness = state.dataIPShortCut->rNumericArgs(10);
     318           58 :         input_specs.total_cooling_capacity_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(2);
     319           58 :         input_specs.total_cooling_capacity_function_of_air_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(3);
     320           58 :         input_specs.energy_input_ratio_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(4);
     321           58 :         input_specs.energy_input_ratio_function_of_air_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(5);
     322           58 :         input_specs.part_load_fraction_correlation_curve_name = state.dataIPShortCut->cAlphaArgs(6);
     323           58 :         input_specs.rated_waste_heat_fraction_of_power_input = state.dataIPShortCut->rNumericArgs(11);
     324           58 :         input_specs.waste_heat_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(7);
     325           58 :         input_specs.sensible_heat_ratio_modifier_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(8);
     326           58 :         input_specs.sensible_heat_ratio_modifier_function_of_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(9);
     327              : 
     328           58 :         this->instantiateFromInputSpec(state, input_specs);
     329           58 :         break;
     330           58 :     }
     331              : 
     332           58 :     if (!found_it) {
     333            0 :         ShowFatalError(state, "Could not find Coil:Cooling:DX:CurveFit:Speed object with name: " + name_to_find);
     334              :     }
     335           58 : }
     336              : 
     337           44 : void CoilCoolingDXCurveFitSpeed::size(EnergyPlus::EnergyPlusData &state)
     338              : {
     339              : 
     340              :     static constexpr std::string_view RoutineName = "sizeSpeed";
     341              : 
     342           44 :     this->rated_total_capacity = this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal * this->parentModeRatedGrossTotalCap;
     343           44 :     this->evap_air_flow_rate = this->original_input_specs.evaporator_air_flow_fraction * this->parentModeRatedEvapAirFlowRate;
     344           44 :     this->condenser_air_flow_rate = this->original_input_specs.condenser_air_flow_fraction * this->parentModeRatedCondAirFlowRate;
     345           44 :     this->grossRatedSHR = this->original_input_specs.gross_rated_sensible_heat_ratio;
     346              : 
     347           44 :     this->RatedAirMassFlowRate =
     348           88 :         this->evap_air_flow_rate *
     349           44 :         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, RatedInletAirTemp, RatedInletAirHumRat, RoutineName);
     350           44 :     this->RatedCondAirMassFlowRate =
     351           88 :         this->condenser_air_flow_rate *
     352           44 :         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, RatedInletAirTemp, RatedInletAirHumRat, RoutineName);
     353              : 
     354           44 :     bool PrintFlag = true;
     355           44 :     bool errorsFound = false;
     356           44 :     std::string CompType = this->object_name;
     357           44 :     std::string CompName = this->name;
     358              : 
     359           44 :     CoolingAirFlowSizer sizingCoolingAirFlow;
     360           44 :     std::string stringOverride = "Rated Air Flow Rate [m3/s]";
     361           44 :     if (state.dataGlobal->isEpJSON) stringOverride = "rated_air_flow_rate [m3/s]";
     362           44 :     std::string preFixString;
     363              :     // if (maxSpeeds > 1) preFixString = "Speed " + std::to_string(speedNum + 1) + " ";
     364              :     // stringOverride = preFixString + stringOverride;
     365           44 :     sizingCoolingAirFlow.overrideSizingString(stringOverride);
     366           44 :     if (this->original_input_specs.evaporator_air_flow_fraction < 1.0) {
     367           24 :         state.dataSize->DataDXCoolsLowSpeedsAutozize = true;
     368           24 :         state.dataSize->DataFractionUsedForSizing = this->original_input_specs.evaporator_air_flow_fraction;
     369              :     }
     370           44 :     sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     371           44 :     this->evap_air_flow_rate = sizingCoolingAirFlow.size(state, this->evap_air_flow_rate, errorsFound);
     372              : 
     373           44 :     std::string SizingString = preFixString + "Gross Cooling Capacity [W]";
     374           44 :     CoolingCapacitySizer sizerCoolingCapacity;
     375           44 :     sizerCoolingCapacity.overrideSizingString(SizingString);
     376           44 :     if (this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal < 1.0) {
     377           26 :         state.dataSize->DataDXCoolsLowSpeedsAutozize = true;
     378           26 :         state.dataSize->DataConstantUsedForSizing = -999.0;
     379           26 :         state.dataSize->DataFlowUsedForSizing = this->parentModeRatedEvapAirFlowRate;
     380           26 :         state.dataSize->DataFractionUsedForSizing = this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal;
     381              :     }
     382           44 :     sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     383           44 :     this->rated_total_capacity = sizerCoolingCapacity.size(state, this->rated_total_capacity, errorsFound);
     384              : 
     385              :     //  DataSizing::DataEMSOverrideON = DXCoil( DXCoilNum ).RatedSHREMSOverrideOn( Mode );
     386              :     //  DataSizing::DataEMSOverride = DXCoil( DXCoilNum ).RatedSHREMSOverrideValue( Mode );
     387           44 :     state.dataSize->DataFlowUsedForSizing = this->evap_air_flow_rate;
     388           44 :     state.dataSize->DataCapacityUsedForSizing = this->rated_total_capacity;
     389           44 :     bool errorFound = false;
     390           44 :     CoolingSHRSizer sizerCoolingSHR;
     391           44 :     sizerCoolingSHR.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     392           44 :     if (this->grossRatedSHR == DataSizing::AutoSize && this->parentOperatingMode == 2) {
     393            0 :         state.dataSize->DataSizingFraction = 0.667;
     394            0 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     395           44 :     } else if (this->grossRatedSHR == DataSizing::AutoSize && this->parentOperatingMode == 3) {
     396            0 :         state.dataSize->DataSizingFraction = 0.333;
     397            0 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     398              :     } else {
     399           44 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     400              :     }
     401           44 :     state.dataSize->DataFlowUsedForSizing = 0.0;
     402           44 :     state.dataSize->DataCapacityUsedForSizing = 0.0;
     403           44 :     state.dataSize->DataTotCapCurveIndex = 0;
     404           44 :     state.dataSize->DataSizingFraction = 1.0;
     405              :     //  DataSizing::DataEMSOverrideON = false;
     406              :     //  DataSizing::DataEMSOverride = 0.0;
     407              : 
     408           44 :     if (this->indexSHRFT > 0 && this->indexSHRFFF > 0) {
     409            4 :         this->RatedCBF = 0.001;
     410              :     } else {
     411              : 
     412           40 :         this->RatedCBF = CalcBypassFactor(state,
     413              :                                           RatedInletAirTemp,
     414              :                                           RatedInletAirHumRat,
     415              :                                           this->rated_total_capacity,
     416              :                                           this->grossRatedSHR,
     417              :                                           Psychrometrics::PsyHFnTdbW(RatedInletAirTemp, RatedInletAirHumRat),
     418              :                                           DataEnvironment::StdPressureSeaLevel);
     419              :     }
     420           44 :     this->RatedEIR = 1.0 / this->original_input_specs.gross_rated_cooling_COP;
     421           44 :     this->ratedLatentCapacity = this->rated_total_capacity * (1.0 - this->grossRatedSHR);
     422              : 
     423              :     // reset for next speed or coil
     424           44 :     state.dataSize->DataConstantUsedForSizing = 0.0;
     425           44 :     state.dataSize->DataDXCoolsLowSpeedsAutozize = false;
     426           44 : }
     427              : 
     428          170 : void CoilCoolingDXCurveFitSpeed::CalcSpeedOutput(EnergyPlus::EnergyPlusData &state,
     429              :                                                  const DataLoopNode::NodeData &inletNode,
     430              :                                                  DataLoopNode::NodeData &outletNode,
     431              :                                                  Real64 const PLR,
     432              :                                                  HVAC::FanOp const fanOp,
     433              :                                                  const Real64 condInletTemp)
     434              : {
     435              : 
     436              :     // SUBROUTINE PARAMETER DEFINITIONS:
     437              :     static constexpr std::string_view RoutineName("CalcSpeedOutput: ");
     438              : 
     439          170 :     if ((PLR == 0.0) || (AirMassFlow == 0.0)) {
     440            2 :         outletNode.Temp = inletNode.Temp;
     441            2 :         outletNode.HumRat = inletNode.HumRat;
     442            2 :         outletNode.Enthalpy = inletNode.Enthalpy;
     443            2 :         outletNode.Press = inletNode.Press;
     444            2 :         fullLoadPower = 0.0;
     445            2 :         fullLoadWasteHeat = 0.0;
     446            2 :         RTF = 0.0;
     447            2 :         return;
     448              :     }
     449              : 
     450              :     Real64 hDelta; // enthalpy difference across cooling coil
     451              :     Real64 A0;     // ratio of UA to Cp
     452              :     Real64 CBF;    // adjusted coil bypass factor
     453          168 :     if (RatedCBF > 0.0) {
     454          168 :         A0 = -std::log(RatedCBF) * RatedAirMassFlowRate;
     455              :     } else {
     456              :         // This is bad - results in CBF = 1.0 which results in divide by zero below: hADP = inletState.h - hDelta / (1.0 - CBF)
     457            0 :         ShowFatalError(state, format("{}Rated CBF={:.6R} is <= 0.0 for {}={}", RoutineName, RatedCBF, object_name, name));
     458            0 :         A0 = 0.0;
     459              :     }
     460          168 :     Real64 ADiff = -A0 / AirMassFlow;
     461          168 :     if (ADiff >= DataPrecisionGlobals::EXP_LowerLimit) {
     462          168 :         CBF = std::exp(ADiff);
     463              :     } else {
     464            0 :         CBF = 0.0;
     465              :     }
     466              : 
     467          168 :     assert(ambPressure > 0.0);
     468          168 :     Real64 inletWetBulb = Psychrometrics::PsyTwbFnTdbWPb(state, inletNode.Temp, inletNode.HumRat, ambPressure);
     469          168 :     Real64 inletw = inletNode.HumRat;
     470              : 
     471          168 :     int Counter = 0;                  // iteration counter for dry coil condition
     472          168 :     int constexpr MaxIter(30);        // iteration limit
     473          168 :     Real64 constexpr Tolerance(0.01); // iteration convergence limit
     474          168 :     Real64 RF = 0.4;                  // relaxation factor for holding back changes in value during iteration
     475              :     Real64 TotCap;
     476              :     Real64 SHR;
     477              :     while (true) {
     478              : 
     479          168 :         Real64 TotCapTempModFac = 1.0;
     480          168 :         if (indexCapFT > 0) {
     481          158 :             if (state.dataCurveManager->curves(indexCapFT)->numDims == 2) {
     482          158 :                 TotCapTempModFac = Curve::CurveValue(state, indexCapFT, inletWetBulb, condInletTemp);
     483              :             } else {
     484            0 :                 TotCapTempModFac = Curve::CurveValue(state, indexCapFT, condInletTemp);
     485              :             }
     486              :         }
     487          168 :         Real64 TotCapFlowModFac = 1.0;
     488          168 :         if (indexCapFFF > 0) {
     489          158 :             TotCapFlowModFac = Curve::CurveValue(state, indexCapFFF, AirFF);
     490              :         }
     491              : 
     492          168 :         TotCap = this->rated_total_capacity * TotCapFlowModFac * TotCapTempModFac;
     493          168 :         hDelta = TotCap / AirMassFlow;
     494              : 
     495          168 :         if (indexSHRFT > 0 && indexSHRFFF > 0) {
     496           90 :             Real64 SHRTempModFrac = max(Curve::CurveValue(state, indexSHRFT, inletWetBulb, inletNode.Temp), 0.0);
     497              : 
     498           90 :             Real64 SHRFlowModFrac = max(Curve::CurveValue(state, indexSHRFFF, AirFF), 0.0);
     499              : 
     500           90 :             SHR = this->grossRatedSHR * SHRTempModFrac * SHRFlowModFrac;
     501           90 :             SHR = max(min(SHR, 1.0), 0.0);
     502           90 :             break;
     503              :         } else {
     504              :             // Calculate apparatus dew point conditions using TotCap and CBF
     505           78 :             Real64 hADP = inletNode.Enthalpy - hDelta / (1.0 - CBF);
     506           78 :             Real64 tADP = Psychrometrics::PsyTsatFnHPb(state, hADP, ambPressure, RoutineName);
     507           78 :             Real64 wADP = Psychrometrics::PsyWFnTdbH(state, tADP, hADP, RoutineName);
     508           78 :             Real64 hTinwADP = Psychrometrics::PsyHFnTdbW(inletNode.Temp, wADP);
     509           78 :             if ((inletNode.Enthalpy - hADP) > 1.e-10) {
     510           78 :                 SHR = min((hTinwADP - hADP) / (inletNode.Enthalpy - hADP), 1.0);
     511              :             } else {
     512            0 :                 SHR = 1.0;
     513              :             }
     514              :             // Check for dry evaporator conditions (win < wadp)
     515           78 :             if (wADP > inletw || (Counter >= 1 && Counter < MaxIter)) {
     516            0 :                 if (inletw == 0.0) inletw = 0.00001;
     517            0 :                 Real64 werror = (inletw - wADP) / inletw;
     518              :                 // Increase InletAirHumRatTemp at constant InletAirTemp to find coil dry-out point. Then use the
     519              :                 // capacity at the dry-out point to determine exiting conditions from coil. This is required
     520              :                 // since the TotCapTempModFac doesn't work properly with dry-coil conditions.
     521            0 :                 inletw = RF * wADP + (1.0 - RF) * inletw;
     522            0 :                 inletWetBulb = Psychrometrics::PsyTwbFnTdbWPb(state, inletNode.Temp, inletw, ambPressure);
     523            0 :                 ++Counter;
     524            0 :                 if (std::abs(werror) > Tolerance) continue; // Recalculate with modified inlet conditions
     525            0 :                 break;
     526              :             } else {
     527              :                 break;
     528              :             }
     529              :         }
     530            0 :     }
     531              : 
     532          168 :     assert(SHR >= 0.0);
     533              : 
     534          168 :     Real64 PLF = 1.0; // part load factor as a function of PLR, RTF = PLR / PLF
     535          168 :     if (indexPLRFPLF > 0) {
     536          158 :         PLF = Curve::CurveValue(state, indexPLRFPLF, PLR); // Calculate part-load factor
     537              :     }
     538          168 :     if (fanOp == HVAC::FanOp::Cycling) state.dataHVACGlobal->OnOffFanPartLoadFraction = PLF;
     539              : 
     540          168 :     Real64 EIRTempModFac = 1.0; // EIR as a function of temperature curve result
     541          168 :     if (indexEIRFT > 0) {
     542          158 :         if (state.dataCurveManager->curves(indexEIRFT)->numDims == 2) {
     543          158 :             EIRTempModFac = Curve::CurveValue(state, indexEIRFT, inletWetBulb, condInletTemp);
     544              :         } else {
     545            0 :             EIRTempModFac = Curve::CurveValue(state, indexEIRFT, condInletTemp);
     546              :         }
     547              :     }
     548          168 :     Real64 EIRFlowModFac = 1.0; // EIR as a function of flow fraction curve result
     549          168 :     if (indexEIRFFF > 0) {
     550          158 :         EIRFlowModFac = Curve::CurveValue(state, indexEIRFFF, AirFF);
     551              :     }
     552              : 
     553          168 :     Real64 wasteHeatTempModFac = 1.0; // waste heat fraction as a function of temperature curve result
     554          168 :     if (indexWHFT > 0) {
     555           46 :         wasteHeatTempModFac = Curve::CurveValue(state, indexWHFT, condInletTemp, inletNode.Temp);
     556              :     }
     557              : 
     558          168 :     Real64 EIR = RatedEIR * EIRFlowModFac * EIRTempModFac;
     559          168 :     RTF = PLR / PLF;
     560          168 :     fullLoadPower = TotCap * EIR;
     561          168 :     fullLoadWasteHeat = ratedWasteHeatFractionOfPowerInput * wasteHeatTempModFac * fullLoadPower;
     562              : 
     563          168 :     outletNode.Enthalpy = inletNode.Enthalpy - hDelta;
     564          168 :     Real64 hTinwout = inletNode.Enthalpy - ((1.0 - SHR) * hDelta);
     565          168 :     outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, inletNode.Temp, hTinwout);
     566          168 :     outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
     567              : 
     568              :     //  If constant fan with cycling compressor, call function to determine "effective SHR"
     569              :     //  which includes the part-load degradation on latent capacity
     570          168 :     if (this->doLatentDegradation && (fanOp == HVAC::FanOp::Continuous)) {
     571            0 :         Real64 QLatActual = TotCap * (1.0 - SHR);
     572              :         // TODO: Figure out HeatingRTF for this
     573            0 :         Real64 HeatingRTF = 0.0;
     574            0 :         SHR = calcEffectiveSHR(inletNode, inletWetBulb, SHR, RTF, ratedLatentCapacity, QLatActual, HeatingRTF);
     575              :         // Calculate full load output conditions
     576            0 :         if (SHR > 1.0) SHR = 1.0;
     577            0 :         hTinwout = inletNode.Enthalpy - (1.0 - SHR) * hDelta;
     578            0 :         if (SHR < 1.0) {
     579            0 :             outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, inletNode.Temp, hTinwout, RoutineName);
     580              :         } else {
     581            0 :             outletNode.HumRat = inletNode.HumRat;
     582              :         }
     583            0 :         outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
     584              :     }
     585              :     // Check for saturation error and modify temperature at constant enthalpy
     586          168 :     Real64 tsat = Psychrometrics::PsyTsatFnHPb(state, outletNode.Enthalpy, inletNode.Press, RoutineName);
     587          168 :     if (outletNode.Temp < tsat) {
     588           66 :         outletNode.Temp = tsat;
     589           66 :         outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, tsat, outletNode.Enthalpy);
     590              :     }
     591              : }
     592              : 
     593           40 : Real64 CoilCoolingDXCurveFitSpeed::CalcBypassFactor(EnergyPlus::EnergyPlusData &state,
     594              :                                                     Real64 const tdb, // Inlet dry-bulb temperature {C}
     595              :                                                     Real64 const w,   // Inlet humidity ratio {kg-H2O/kg-dryair}
     596              :                                                     Real64 const q,   // Total capacity {W}
     597              :                                                     Real64 const shr, // SHR
     598              :                                                     Real64 const h,   // Inlet enthalpy {J/kg-dryair}
     599              :                                                     Real64 const p)   // Outlet node pressure {Pa}
     600              : {
     601              : 
     602              :     static constexpr std::string_view RoutineName("CalcBypassFactor: ");
     603           40 :     Real64 constexpr SmallDifferenceTest(0.00000001);
     604              : 
     605              :     // Bypass factors are calculated at rated conditions at sea level (make sure in.p is Standard Pressure)
     606              :     Real64 calcCBF;
     607              : 
     608           40 :     Real64 airMassFlowRate = this->evap_air_flow_rate * Psychrometrics::PsyRhoAirFnPbTdbW(state, p, tdb, w);
     609           40 :     Real64 deltaH = q / airMassFlowRate;
     610           40 :     Real64 outp = p;
     611           40 :     Real64 outh = h - deltaH;
     612           40 :     Real64 outw = Psychrometrics::PsyWFnTdbH(state, tdb, h - (1.0 - shr) * deltaH); // enthalpy at Tdb,in and Wout
     613           40 :     Real64 outtdb = Psychrometrics::PsyTdbFnHW(outh, outw);
     614           40 :     Real64 outrh = Psychrometrics::PsyRhFnTdbWPb(state, outtdb, outw, outp);
     615              : 
     616           40 :     if (outrh >= 1.0) {
     617            4 :         ShowWarningError(state, std::string{RoutineName} + ": For object = " + this->object_name + ", name = \"" + this->name + "\"");
     618            4 :         ShowContinueError(state, "Calculated outlet air relative humidity greater than 1. The combination of");
     619            4 :         ShowContinueError(state, "rated air volume flow rate, total cooling capacity and sensible heat ratio yields coil exiting");
     620            4 :         ShowContinueError(state, "air conditions above the saturation curve. Possible fixes are to reduce the rated total cooling");
     621            4 :         ShowContinueError(state, "capacity, increase the rated air volume flow rate, or reduce the rated sensible heat ratio for this coil.");
     622            4 :         ShowContinueError(state, "If autosizing, it is recommended that all three of these values be autosized.");
     623            4 :         ShowContinueError(state, "...Inputs used for calculating cooling coil bypass factor.");
     624            2 :         ShowContinueError(state, format("...Inlet Air Temperature     = {:.2R} C", tdb));
     625            2 :         ShowContinueError(state, format("...Outlet Air Temperature    = {:.2R} C", outtdb));
     626            2 :         ShowContinueError(state, format("...Inlet Air Humidity Ratio  = {:.6R} kgWater/kgDryAir", w));
     627            2 :         ShowContinueError(state, format("...Outlet Air Humidity Ratio = {:.6R} kgWater/kgDryAir", outw));
     628            2 :         ShowContinueError(state, format("...Total Cooling Capacity used in calculation = {:.2R} W", q));
     629            2 :         ShowContinueError(state, format("...Air Mass Flow Rate used in calculation     = {:.6R} kg/s", airMassFlowRate));
     630            2 :         ShowContinueError(state, format("...Air Volume Flow Rate used in calculation   = {:.6R} m3/s", this->evap_air_flow_rate));
     631            2 :         if (q > 0.0) {
     632            2 :             if (((this->minRatedVolFlowPerRatedTotCap - this->evap_air_flow_rate / q) > SmallDifferenceTest) ||
     633            0 :                 ((this->evap_air_flow_rate / q - this->maxRatedVolFlowPerRatedTotCap) > SmallDifferenceTest)) {
     634            4 :                 ShowContinueError(state,
     635            4 :                                   format("...Air Volume Flow Rate per Watt of Rated Cooling Capacity is also out of bounds at = {:.7R} m3/s/W",
     636            4 :                                          this->evap_air_flow_rate / q));
     637              :             }
     638              :         }
     639            2 :         Real64 outletAirTempSat = Psychrometrics::PsyTsatFnHPb(state, outh, outp, RoutineName);
     640            2 :         if (outtdb < outletAirTempSat) { // Limit to saturated conditions at OutletAirEnthalpy
     641            2 :             outtdb = outletAirTempSat + 0.005;
     642            2 :             outw = Psychrometrics::PsyWFnTdbH(state, outtdb, outh, RoutineName);
     643            2 :             Real64 adjustedSHR = (Psychrometrics::PsyHFnTdbW(tdb, outw) - outh) / deltaH;
     644            4 :             ShowWarningError(state,
     645            6 :                              std::string{RoutineName} + object_name + " \"" + name +
     646              :                                  "\", SHR adjusted to achieve valid outlet air properties and the simulation continues.");
     647            2 :             ShowContinueError(state, format("Initial SHR = {:.5R}", this->grossRatedSHR));
     648            2 :             ShowContinueError(state, format("Adjusted SHR = {:.5R}", adjustedSHR));
     649              :         }
     650              :     }
     651              : 
     652              :     // ADP conditions
     653           40 :     Real64 adp_tdb = Psychrometrics::PsyTdpFnWPb(state, outw, outp);
     654              : 
     655           40 :     Real64 deltaT = tdb - outtdb;
     656           40 :     Real64 deltaHumRat = w - outw;
     657           40 :     Real64 slopeAtConds = 0.0;
     658           40 :     if (deltaT > 0.0) slopeAtConds = deltaHumRat / deltaT;
     659           40 :     if (slopeAtConds <= 0.0) {
     660            0 :         ShowSevereError(state, this->object_name + " \"" + this->name + "\"");
     661            0 :         ShowContinueError(state, "...Invalid slope or outlet air condition when calculating cooling coil bypass factor.");
     662            0 :         ShowContinueError(state, format("...Slope = {:.8R}", slopeAtConds));
     663            0 :         ShowContinueError(state, format("...Inlet Air Temperature     = {:.2R} C", tdb));
     664            0 :         ShowContinueError(state, format("...Outlet Air Temperature    = {:.2R} C", outtdb));
     665            0 :         ShowContinueError(state, format("...Inlet Air Humidity Ratio  = {:.6R} kgWater/kgDryAir", w));
     666            0 :         ShowContinueError(state, format("...Outlet Air Humidity Ratio = {:.6R} kgWater/kgDryAir", outw));
     667            0 :         ShowContinueError(state, format("...Total Cooling Capacity used in calculation = {:.2R} W", q));
     668            0 :         ShowContinueError(state, format("...Air Mass Flow Rate used in calculation     = {:.6R} kg/s", airMassFlowRate));
     669            0 :         ShowContinueError(state, format("...Air Volume Flow Rate used in calculation   = {:.6R} m3/s", this->evap_air_flow_rate));
     670            0 :         if (q > 0.0) {
     671            0 :             if (((this->minRatedVolFlowPerRatedTotCap - this->evap_air_flow_rate / q) > SmallDifferenceTest) ||
     672            0 :                 ((this->evap_air_flow_rate / q - this->maxRatedVolFlowPerRatedTotCap) > SmallDifferenceTest)) {
     673            0 :                 ShowContinueError(state,
     674            0 :                                   format("...Air Volume Flow Rate per Watt of Rated Cooling Capacity is also out of bounds at = {:.7R} m3/s/W",
     675            0 :                                          this->evap_air_flow_rate / q));
     676              :             }
     677              :         }
     678            0 :         ShowFatalError(state, "Errors found in calculating coil bypass factors");
     679              :     }
     680              : 
     681           40 :     Real64 adp_w = min(outw, Psychrometrics::PsyWFnTdpPb(state, adp_tdb, DataEnvironment::StdPressureSeaLevel));
     682              : 
     683           40 :     int iter = 0;
     684           40 :     int constexpr maxIter(50);
     685           40 :     Real64 errorLast = 100.0;
     686           40 :     Real64 deltaADPTemp = 5.0;
     687           40 :     Real64 tolerance = 1.0; // initial conditions for iteration
     688           40 :     bool cbfErrors = false;
     689          398 :     while ((iter <= maxIter) && (tolerance > 0.001)) {
     690              : 
     691              :         // Do for IterMax iterations or until the error gets below .1%
     692          358 :         if (iter > 0) adp_tdb += deltaADPTemp;
     693          358 :         ++iter;
     694              :         //  Find new slope using guessed Tadp
     695          358 :         adp_w = min(outw, Psychrometrics::PsyWFnTdpPb(state, adp_tdb, DataEnvironment::StdPressureSeaLevel));
     696          358 :         Real64 slope = (w - adp_w) / max(0.001, (tdb - adp_tdb));
     697              :         //  check for convergence (slopes are equal to within error tolerance)
     698          358 :         Real64 error = (slope - slopeAtConds) / slopeAtConds;
     699          358 :         if ((error > 0.0) && (errorLast < 0.0)) {
     700          112 :             deltaADPTemp = -deltaADPTemp / 2.0;
     701          246 :         } else if ((error < 0.0) && (errorLast > 0.0)) {
     702          136 :             deltaADPTemp = -deltaADPTemp / 2.0;
     703          110 :         } else if (std::abs(error) > std::abs(errorLast)) {
     704            0 :             deltaADPTemp = -deltaADPTemp / 2.0;
     705              :         }
     706          358 :         errorLast = error;
     707          358 :         tolerance = std::abs(error);
     708              :     }
     709              : 
     710              :     //   Calculate Bypass Factor from Enthalpies
     711           40 :     Real64 adp_h = Psychrometrics::PsyHFnTdbW(adp_tdb, adp_w);
     712           40 :     calcCBF = min(1.0, (outh - adp_h) / (h - adp_h));
     713              : 
     714           40 :     if (iter > maxIter) {
     715            0 :         ShowSevereError(state,
     716            0 :                         std::string{RoutineName} + object_name + " \"" + name +
     717              :                             "\" -- coil bypass factor calculation did not converge after max iterations.");
     718            0 :         ShowContinueError(state, format("The RatedSHR of [{:.3R}], entered by the user or autosized (see *.eio file),", this->grossRatedSHR));
     719            0 :         ShowContinueError(state, "may be causing this. The line defined by the coil rated inlet air conditions");
     720            0 :         ShowContinueError(state, "(26.7C drybulb and 19.4C wetbulb) and the RatedSHR (i.e., slope of the line) must intersect");
     721            0 :         ShowContinueError(state, "the saturation curve of the psychrometric chart. If the RatedSHR is too low, then this");
     722            0 :         ShowContinueError(state, "intersection may not occur and the coil bypass factor calculation will not converge.");
     723            0 :         ShowContinueError(state, "If autosizing the SHR, recheck the design supply air humidity ratio and design supply air");
     724            0 :         ShowContinueError(state, "temperature values in the Sizing:System and Sizing:Zone objects. In general, the temperatures");
     725            0 :         ShowContinueError(state, "and humidity ratios specified in these two objects should be the same for each system");
     726            0 :         ShowContinueError(state, "and the zones that it serves.");
     727            0 :         ShowContinueErrorTimeStamp(state, "");
     728            0 :         cbfErrors = true; // Didn't converge within MaxIter iterations
     729              :     }
     730           40 :     if (calcCBF < 0.0) {
     731            0 :         ShowSevereError(state, std::string{RoutineName} + object_name + " \"" + name + "\" -- negative coil bypass factor calculated.");
     732            0 :         ShowContinueErrorTimeStamp(state, "");
     733            0 :         cbfErrors = true; // Negative CBF not valid
     734              :     }
     735              :     // Show fatal error for specific coil that caused a CBF error
     736           40 :     if (cbfErrors) {
     737            0 :         ShowFatalError(state, std::string{RoutineName} + object_name + " \"" + name + "\" Errors found in calculating coil bypass factors");
     738              :     }
     739           40 :     return calcCBF;
     740              : }
     741              : 
     742            0 : Real64 CoilCoolingDXCurveFitSpeed::calcEffectiveSHR(const DataLoopNode::NodeData &inletNode,
     743              :                                                     Real64 const inletWetBulb,
     744              :                                                     Real64 const SHRss,      // Steady-state sensible heat ratio
     745              :                                                     Real64 const RTF,        // Compressor run-time fraction
     746              :                                                     Real64 const QLatRated,  // Rated latent capacity
     747              :                                                     Real64 const QLatActual, // Actual latent capacity
     748              :                                                     Real64 const HeatingRTF  // Used to recalculate Toff for cycling fan systems
     749              : )
     750              : {
     751              :     // PURPOSE OF THIS FUNCTION:
     752              :     //    Adjust sensible heat ratio to account for degradation of DX coil latent
     753              :     //    capacity at part-load (cycling) conditions.
     754              : 
     755              :     // METHODOLOGY EMPLOYED:
     756              :     //    With model parameters entered by the user, the part-load latent performance
     757              :     //    of a DX cooling coil is determined for a constant air flow system with
     758              :     //    a cooling coil that cycles on/off. The model calculates the time
     759              :     //    required for condensate to begin falling from the cooling coil.
     760              :     //    Runtimes greater than this are integrated to a "part-load" latent
     761              :     //    capacity which is used to determine the "part-load" sensible heat ratio.
     762              :     //    See reference below for additional details (linear decay model, Eq. 8b).
     763              :     // REFERENCES:
     764              :     //   "A Model to Predict the Latent Capacity of Air Conditioners and
     765              :     //    Heat Pumps at Part-Load Conditions with Constant Fan Operation"
     766              :     //    1996 ASHRAE Transactions, Volume 102, Part 1, Pp. 266 - 274,
     767              :     //    Hugh I. Henderson, Jr., P.E., Kannan Rengarajan, P.E.
     768              : 
     769              :     // Return value
     770              :     Real64 SHReff; // Effective sensible heat ratio, includes degradation due to cycling effects
     771              : 
     772              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     773              :     Real64 Twet; // Nominal time for condensate to begin leaving the coil's condensate drain line
     774              :     //   at the current operating conditions (sec)
     775              :     Real64 Gamma; // Initial moisture evaporation rate divided by steady-state AC latent capacity
     776              :     //   at the current operating conditions
     777              :     Real64 Twet_max; // Maximum allowed value for Twet
     778              :     Real64 Ton;      // Coil on time (sec)
     779              :     Real64 Toff;     // Coil off time (sec)
     780              :     Real64 Toffa;    // Actual coil off time (sec). Equations valid for Toff <= (2.0 * Twet/Gamma)
     781              :     Real64 aa;       // Intermediate variable
     782              :     Real64 To1;      // Intermediate variable (first guess at To). To = time to the start of moisture removal
     783              :     Real64 To2;      // Intermediate variable (second guess at To). To = time to the start of moisture removal
     784              :     Real64 Error;    // Error for iteration (DO) loop
     785              :     Real64 LHRmult;  // Latent Heat Ratio (LHR) multiplier. The effective latent heat ratio LHR = (1-SHRss)*LHRmult
     786              :     Real64 Ton_heating;
     787              :     Real64 Toff_heating;
     788              : 
     789            0 :     Real64 Twet_Rated = parentModeTimeForCondensateRemoval; // Time wet at rated conditions (sec)
     790            0 :     Real64 Gamma_Rated = parentModeEvapRateRatio;           // Gamma at rated conditions
     791            0 :     Real64 Nmax = parentModeMaxCyclingRate;                 // Maximum ON/OFF cycles for the compressor (cycles/hr)
     792            0 :     Real64 Tcl = parentModeLatentTimeConst;                 // Time constant for latent capacity to reach steady state after startup(sec)
     793              : 
     794              :     //  No moisture evaporation (latent degradation) occurs for runtime fraction of 1.0
     795              :     //  All latent degradation model parameters cause divide by 0.0 if not greater than 0.0
     796              :     //  Latent degradation model parameters initialize to 0.0 meaning no evaporation model used.
     797            0 :     if (RTF >= 1.0) {
     798            0 :         SHReff = SHRss;
     799            0 :         return SHReff;
     800              :     }
     801              : 
     802            0 :     Twet_max = 9999.0; // high limit for Twet
     803              : 
     804              :     //  Calculate the model parameters at the actual operating conditions
     805            0 :     Twet = min(Twet_Rated * QLatRated / (QLatActual + 1.e-10), Twet_max);
     806            0 :     Gamma = Gamma_Rated * QLatRated * (inletNode.Temp - inletWetBulb) / ((26.7 - 19.4) * QLatActual + 1.e-10);
     807              : 
     808              :     //  Calculate the compressor on and off times using a converntional thermostat curve
     809            0 :     Ton = 3600.0 / (4.0 * Nmax * (1.0 - RTF)); // duration of cooling coil on-cycle (sec)
     810            0 :     Toff = 3600.0 / (4.0 * Nmax * RTF);        // duration of cooling coil off-cycle (sec)
     811              : 
     812              :     //  Cap Toff to meet the equation restriction
     813            0 :     if (Gamma > 0.0) {
     814            0 :         Toffa = min(Toff, 2.0 * Twet / Gamma);
     815              :     } else {
     816            0 :         Toffa = Toff;
     817              :     }
     818              : 
     819              :     //  Need to include the reheat coil operation to account for actual fan run time. E+ uses a
     820              :     //  separate heating coil for heating and reheat (to separate the heating and reheat loads)
     821              :     //  and real world applications would use a single heating coil for both purposes, the actual
     822              :     //  fan operation is based on HeatingPLR + ReheatPLR. For cycling fan RH control, latent
     823              :     //  degradation only occurs when a heating load exists, in this case the reheat load is
     824              :     //  equal to and oposite in magnitude to the cooling coil sensible output but the reheat
     825              :     //  coil is not always active. This additional fan run time has not been accounted for at this time.
     826              :     //  Recalculate Toff for cycling fan systems when heating is active
     827            0 :     if (HeatingRTF > 0.0) {
     828            0 :         if (HeatingRTF < 1.0 && HeatingRTF > RTF) {
     829            0 :             Ton_heating = 3600.0 / (4.0 * Nmax * (1.0 - HeatingRTF));
     830            0 :             Toff_heating = 3600.0 / (4.0 * Nmax * HeatingRTF);
     831              :             //    add additional heating coil operation during cooling coil off cycle (due to cycling rate difference of coils)
     832            0 :             Ton_heating += max(0.0, min(Ton_heating, (Ton + Toffa) - (Ton_heating + Toff_heating)));
     833            0 :             Toffa = min(Toffa, Ton_heating - Ton);
     834              :         }
     835              :     }
     836              : 
     837              :     //  Use sucessive substitution to solve for To
     838            0 :     aa = (Gamma * Toffa) - (0.25 / Twet) * pow_2(Gamma) * pow_2(Toffa);
     839            0 :     To1 = aa + Tcl;
     840            0 :     Error = 1.0;
     841            0 :     while (Error > 0.001) {
     842            0 :         To2 = aa - Tcl * std::expm1(-To1 / Tcl);
     843            0 :         Error = std::abs((To2 - To1) / To1);
     844            0 :         To1 = To2;
     845              :     }
     846              : 
     847              :     //  Adjust Sensible Heat Ratio (SHR) using Latent Heat Ratio (LHR) multiplier
     848              :     //  Floating underflow errors occur when -Ton/Tcl is a large negative number.
     849              :     //  Cap lower limit at -700 to avoid the underflow errors.
     850            0 :     aa = std::exp(max(-700.0, -Ton / Tcl));
     851              :     //  Calculate latent heat ratio multiplier
     852            0 :     LHRmult = max(((Ton - To2) / (Ton + Tcl * (aa - 1.0))), 0.0);
     853              : 
     854              :     //  Calculate part-load or "effective" sensible heat ratio
     855            0 :     SHReff = 1.0 - (1.0 - SHRss) * LHRmult;
     856              : 
     857            0 :     if (SHReff < SHRss) SHReff = SHRss; // Effective SHR can be less than the steady-state SHR
     858            0 :     if (SHReff > 1.0) SHReff = 1.0;     // Effective sensible heat ratio can't be greater than 1.0
     859              : 
     860            0 :     return SHReff;
     861              : }
        

Generated by: LCOV version 2.0-1