LCOV - code coverage report
Current view: top level - EnergyPlus/Coils - CoilCoolingDXCurveFitSpeed.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 351 451 77.8 %
Date: 2024-08-23 23:50:59 Functions: 7 7 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : #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         128 : void CoilCoolingDXCurveFitSpeed::instantiateFromInputSpec(EnergyPlus::EnergyPlusData &state,
      68             :                                                           const CoilCoolingDXCurveFitSpeedInputSpecification &input_data)
      69             : {
      70         128 :     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         128 :     this->original_input_specs = input_data;
      74         128 :     this->name = input_data.name;
      75         128 :     this->active_fraction_of_face_coil_area = input_data.active_fraction_of_coil_face_area;
      76         128 :     if (this->active_fraction_of_face_coil_area < 1.0) this->adjustForFaceArea = true;
      77         128 :     this->rated_evap_fan_power_per_volume_flow_rate = input_data.rated_evaporator_fan_power_per_volume_flow_rate;
      78         128 :     this->rated_evap_fan_power_per_volume_flow_rate_2023 = input_data.rated_evaporator_fan_power_per_volume_flow_rate_2023;
      79         128 :     this->evap_condenser_pump_power_fraction = input_data.rated_evaporative_condenser_pump_power_fraction;
      80         128 :     this->evap_condenser_effectiveness = input_data.evaporative_condenser_effectiveness;
      81         128 :     this->ratedWasteHeatFractionOfPowerInput = input_data.rated_waste_heat_fraction_of_power_input;
      82         128 :     this->ratedCOP = input_data.gross_rated_cooling_COP;
      83         256 :     errorsFound |= this->processCurve(state,
      84         128 :                                       input_data.total_cooling_capacity_function_of_temperature_curve_name,
      85         128 :                                       this->indexCapFT,
      86             :                                       {1, 2}, // MULTIPLECURVEDIMS
      87             :                                       routineName,
      88             :                                       "Total Cooling Capacity Function of Temperature Curve Name",
      89             :                                       RatedInletWetBulbTemp,
      90         128 :                                       RatedOutdoorAirTemp);
      91             : 
      92         256 :     errorsFound |= this->processCurve(state,
      93         128 :                                       input_data.total_cooling_capacity_function_of_air_flow_fraction_curve_name,
      94         128 :                                       this->indexCapFFF,
      95             :                                       {1},
      96             :                                       routineName,
      97             :                                       "Total Cooling Capacity Function of Air Flow Fraction Curve Name",
      98             :                                       1.0);
      99             : 
     100         256 :     errorsFound |= this->processCurve(state,
     101         128 :                                       input_data.energy_input_ratio_function_of_temperature_curve_name,
     102         128 :                                       this->indexEIRFT,
     103             :                                       {1, 2}, // MULTIPLECURVEDIMS
     104             :                                       routineName,
     105             :                                       "Energy Input Ratio Function of Temperature Curve Name",
     106             :                                       RatedInletWetBulbTemp,
     107         128 :                                       RatedOutdoorAirTemp);
     108             : 
     109         256 :     errorsFound |= this->processCurve(state,
     110         128 :                                       input_data.energy_input_ratio_function_of_air_flow_fraction_curve_name,
     111         128 :                                       this->indexEIRFFF,
     112             :                                       {1},
     113             :                                       routineName,
     114             :                                       "Energy Input Ratio Function of Air Flow Fraction Curve Name",
     115             :                                       1.0);
     116             : 
     117         256 :     errorsFound |= this->processCurve(state,
     118         128 :                                       input_data.sensible_heat_ratio_modifier_function_of_temperature_curve_name,
     119         128 :                                       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         128 :                                       RatedOutdoorAirTemp);
     125             : 
     126         256 :     errorsFound |= this->processCurve(state,
     127         128 :                                       input_data.sensible_heat_ratio_modifier_function_of_flow_fraction_curve_name,
     128         128 :                                       this->indexSHRFFF,
     129             :                                       {1},
     130             :                                       routineName,
     131             :                                       "Sensible Heat Ratio Modifier Function of Air Flow Fraction Curve Name",
     132             :                                       1.0);
     133             : 
     134         256 :     errorsFound |= this->processCurve(state,
     135         128 :                                       input_data.waste_heat_function_of_temperature_curve_name,
     136         128 :                                       this->indexWHFT,
     137             :                                       {2},
     138             :                                       routineName,
     139             :                                       "Waste Heat Modifier Function of Temperature Curve Name",
     140             :                                       RatedOutdoorAirTemp,
     141         128 :                                       RatedInletAirTemp);
     142             : 
     143         128 :     if (!errorsFound && !input_data.waste_heat_function_of_temperature_curve_name.empty()) {
     144          66 :         Real64 CurveVal = Curve::CurveValue(state, this->indexWHFT, RatedOutdoorAirTemp, RatedInletAirTemp);
     145          66 :         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         256 :     errorsFound |= this->processCurve(state,
     156         128 :                                       input_data.part_load_fraction_correlation_curve_name,
     157         128 :                                       this->indexPLRFPLF,
     158             :                                       {1},
     159             :                                       routineName,
     160             :                                       "Part Load Fraction Correlation Curve Name",
     161             :                                       1.0);
     162             : 
     163         128 :     if (this->indexPLRFPLF > 0 && !errorsFound) {
     164             :         //     Test PLF curve minimum and maximum. Cap if less than 0.7 or greater than 1.0.
     165         128 :         Real64 MinCurveVal = 999.0;
     166         128 :         Real64 MaxCurveVal = -999.0;
     167         128 :         Real64 CurveInput = 0.0;
     168         128 :         Real64 MinCurvePLR = 0.0, MaxCurvePLR = 0.0;
     169       12928 :         while (CurveInput <= 1.0) {
     170       12800 :             Real64 CurveVal = Curve::CurveValue(state, this->indexPLRFPLF, CurveInput);
     171       12800 :             if (CurveVal < MinCurveVal) {
     172         128 :                 MinCurveVal = CurveVal;
     173         128 :                 MinCurvePLR = CurveInput;
     174             :             }
     175       12800 :             if (CurveVal > MaxCurveVal) {
     176       12701 :                 MaxCurveVal = CurveVal;
     177       12701 :                 MaxCurvePLR = CurveInput;
     178             :             }
     179       12800 :             CurveInput += 0.01;
     180             :         }
     181         128 :         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         128 :         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         128 :     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         128 : }
     203             : 
     204        1024 : 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        1024 :     if (curveName.empty()) {
     214         312 :         return false;
     215             :     } else {
     216         712 :         curveIndex = Curve::GetCurveIndex(state, curveName);
     217         712 :         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        1424 :             bool errorFound = Curve::CheckCurveDims(state,
     224             :                                                     curveIndex,           // Curve index
     225         712 :                                                     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         712 :             if (!errorFound) {
     231         712 :                 if (Var2.present()) {
     232         650 :                     Curve::checkCurveIsNormalizedToOne(
     233         650 :                         state, std::string{routineName} + this->object_name, this->name, curveIndex, fieldName, curveName, Var1, Var2);
     234             :                 } else {
     235         387 :                     Curve::checkCurveIsNormalizedToOne(
     236         774 :                         state, std::string{routineName} + this->object_name, this->name, curveIndex, fieldName, curveName, Var1);
     237             :                 }
     238             :             }
     239         712 :             return errorFound;
     240             :         }
     241             :     }
     242             : }
     243             : 
     244         128 : CoilCoolingDXCurveFitSpeed::CoilCoolingDXCurveFitSpeed(EnergyPlus::EnergyPlusData &state, const std::string &name_to_find)
     245             :     : // model inputs
     246         128 :       indexCapFT(0), indexCapFFF(0), indexEIRFT(0), indexEIRFFF(0), indexPLRFPLF(0), indexWHFT(0), indexSHRFT(0), indexSHRFFF(0),
     247             : 
     248             :       // speed class inputs
     249         128 :       RatedAirMassFlowRate(0.0),     // rated air mass flow rate at speed {kg/s}
     250         128 :       RatedCondAirMassFlowRate(0.0), // condenser air mass flow rate at speed {kg/s}
     251         128 :       grossRatedSHR(0.0),            // rated sensible heat ratio at speed
     252         128 :       RatedCBF(0.0),                 // rated coil bypass factor at speed
     253         128 :       RatedEIR(0.0),                 // rated energy input ratio at speed {W/W}
     254         128 :       ratedCOP(0.0), rated_total_capacity(0.0), rated_evap_fan_power_per_volume_flow_rate(0.0),
     255         128 :       ratedWasteHeatFractionOfPowerInput(0.0), // rated waste heat fraction of power input
     256         128 :       evap_condenser_pump_power_fraction(0.0), evap_condenser_effectiveness(0.0),
     257             : 
     258         128 :       parentModeRatedGrossTotalCap(0.0), parentModeRatedEvapAirFlowRate(0.0), parentModeRatedCondAirFlowRate(0.0), parentOperatingMode(0),
     259             : 
     260         128 :       ambPressure(0.0), // outdoor pressure {Pa}
     261         128 :       PLR(0.0),         // coil operating part load ratio
     262         128 :       AirFF(0.0),       // ratio of air mass flow rate to rated air mass flow rate
     263             :                         // RatedTotCap( 0.0 ), // rated total capacity at speed {W}
     264             : 
     265         128 :       fullLoadPower(0.0),     // full load power at speed {W}
     266         128 :       fullLoadWasteHeat(0.0), // full load waste heat at speed {W}
     267         128 :       RTF(0.0),               // coil runtime fraction at speed
     268         128 :       AirMassFlow(0.0),       // coil inlet air mass flow rate {kg/s}
     269             : 
     270             :       // other data members
     271         128 :       evap_air_flow_rate(0.0), condenser_air_flow_rate(0.0), active_fraction_of_face_coil_area(0.0),
     272         128 :       ratedLatentCapacity(0.0), // Latent capacity at rated conditions {W}
     273             : 
     274             :       // rating data
     275         128 :       RatedInletAirTemp(26.6667),       // 26.6667C or 80F
     276         128 :       RatedInletWetBulbTemp(19.4444),   // 19.44 or 67F
     277         128 :       RatedInletAirHumRat(0.0111847),   // Humidity ratio corresponding to 80F dry bulb/67F wet bulb
     278         128 :       RatedOutdoorAirTemp(35.0),        // 35 C or 95F
     279         128 :       DryCoilOutletHumRatioMin(0.00001) // dry coil outlet minimum hum ratio kgH2O/kgdry air
     280             : 
     281             : {
     282         128 :     int numSpeeds = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CoilCoolingDXCurveFitSpeed::object_name);
     283             :     if (numSpeeds <= 0) {
     284             :         // error
     285             :     }
     286         128 :     bool found_it = false;
     287         891 :     for (int speedNum = 1; speedNum <= numSpeeds; ++speedNum) {
     288             :         int NumAlphas;  // Number of Alphas for each GetObjectItem call
     289             :         int NumNumbers; // Number of Numbers for each GetObjectItem call
     290             :         int IOStatus;
     291        1782 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     292             :                                                                  CoilCoolingDXCurveFitSpeed::object_name,
     293             :                                                                  speedNum,
     294         891 :                                                                  state.dataIPShortCut->cAlphaArgs,
     295             :                                                                  NumAlphas,
     296         891 :                                                                  state.dataIPShortCut->rNumericArgs,
     297             :                                                                  NumNumbers,
     298             :                                                                  IOStatus);
     299         891 :         if (!Util::SameString(name_to_find, state.dataIPShortCut->cAlphaArgs(1))) {
     300         763 :             continue;
     301             :         }
     302         128 :         found_it = true;
     303             : 
     304         128 :         CoilCoolingDXCurveFitSpeedInputSpecification input_specs;
     305             : 
     306         128 :         input_specs.name = state.dataIPShortCut->cAlphaArgs(1);
     307         128 :         input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal = state.dataIPShortCut->rNumericArgs(1);
     308         128 :         input_specs.evaporator_air_flow_fraction = state.dataIPShortCut->rNumericArgs(2);
     309         128 :         input_specs.condenser_air_flow_fraction = state.dataIPShortCut->rNumericArgs(3);
     310         128 :         input_specs.gross_rated_sensible_heat_ratio = state.dataIPShortCut->rNumericArgs(4);
     311         128 :         input_specs.gross_rated_cooling_COP = state.dataIPShortCut->rNumericArgs(5);
     312         128 :         input_specs.active_fraction_of_coil_face_area = state.dataIPShortCut->rNumericArgs(6);
     313         128 :         input_specs.rated_evaporator_fan_power_per_volume_flow_rate = state.dataIPShortCut->rNumericArgs(7);
     314         128 :         input_specs.rated_evaporator_fan_power_per_volume_flow_rate_2023 = state.dataIPShortCut->rNumericArgs(8);
     315         128 :         input_specs.rated_evaporative_condenser_pump_power_fraction = state.dataIPShortCut->rNumericArgs(9);
     316         128 :         input_specs.evaporative_condenser_effectiveness = state.dataIPShortCut->rNumericArgs(10);
     317         128 :         input_specs.total_cooling_capacity_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(2);
     318         128 :         input_specs.total_cooling_capacity_function_of_air_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(3);
     319         128 :         input_specs.energy_input_ratio_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(4);
     320         128 :         input_specs.energy_input_ratio_function_of_air_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(5);
     321         128 :         input_specs.part_load_fraction_correlation_curve_name = state.dataIPShortCut->cAlphaArgs(6);
     322         128 :         input_specs.rated_waste_heat_fraction_of_power_input = state.dataIPShortCut->rNumericArgs(11);
     323         128 :         input_specs.waste_heat_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(7);
     324         128 :         input_specs.sensible_heat_ratio_modifier_function_of_temperature_curve_name = state.dataIPShortCut->cAlphaArgs(8);
     325         128 :         input_specs.sensible_heat_ratio_modifier_function_of_flow_fraction_curve_name = state.dataIPShortCut->cAlphaArgs(9);
     326             : 
     327         128 :         this->instantiateFromInputSpec(state, input_specs);
     328         128 :         break;
     329         128 :     }
     330             : 
     331         128 :     if (!found_it) {
     332           0 :         ShowFatalError(state, "Could not find Coil:Cooling:DX:CurveFit:Speed object with name: " + name_to_find);
     333             :     }
     334         128 : }
     335             : 
     336         128 : void CoilCoolingDXCurveFitSpeed::size(EnergyPlus::EnergyPlusData &state)
     337             : {
     338             : 
     339             :     static constexpr std::string_view RoutineName = "sizeSpeed";
     340             : 
     341         128 :     this->rated_total_capacity = this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal * this->parentModeRatedGrossTotalCap;
     342         128 :     this->evap_air_flow_rate = this->original_input_specs.evaporator_air_flow_fraction * this->parentModeRatedEvapAirFlowRate;
     343         128 :     this->condenser_air_flow_rate = this->original_input_specs.condenser_air_flow_fraction * this->parentModeRatedCondAirFlowRate;
     344         128 :     this->grossRatedSHR = this->original_input_specs.gross_rated_sensible_heat_ratio;
     345             : 
     346         128 :     this->RatedAirMassFlowRate =
     347         256 :         this->evap_air_flow_rate *
     348         128 :         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, RatedInletAirTemp, RatedInletAirHumRat, RoutineName);
     349         128 :     this->RatedCondAirMassFlowRate =
     350         256 :         this->condenser_air_flow_rate *
     351         128 :         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, RatedInletAirTemp, RatedInletAirHumRat, RoutineName);
     352             : 
     353         128 :     bool PrintFlag = true;
     354         128 :     bool errorsFound = false;
     355         128 :     std::string CompType = this->object_name;
     356         128 :     std::string CompName = this->name;
     357             : 
     358         128 :     CoolingAirFlowSizer sizingCoolingAirFlow;
     359         128 :     std::string stringOverride = "Rated Air Flow Rate [m3/s]";
     360         128 :     if (state.dataGlobal->isEpJSON) stringOverride = "rated_air_flow_rate [m3/s]";
     361         128 :     std::string preFixString;
     362             :     // if (maxSpeeds > 1) preFixString = "Speed " + std::to_string(speedNum + 1) + " ";
     363             :     // stringOverride = preFixString + stringOverride;
     364         128 :     sizingCoolingAirFlow.overrideSizingString(stringOverride);
     365         128 :     if (this->original_input_specs.evaporator_air_flow_fraction < 1.0) {
     366          67 :         state.dataSize->DataDXCoolsLowSpeedsAutozize = true;
     367          67 :         state.dataSize->DataFractionUsedForSizing = this->original_input_specs.evaporator_air_flow_fraction;
     368             :     }
     369         128 :     sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     370         128 :     this->evap_air_flow_rate = sizingCoolingAirFlow.size(state, this->evap_air_flow_rate, errorsFound);
     371             : 
     372         128 :     std::string SizingString = preFixString + "Gross Cooling Capacity [W]";
     373         128 :     CoolingCapacitySizer sizerCoolingCapacity;
     374         128 :     sizerCoolingCapacity.overrideSizingString(SizingString);
     375         128 :     if (this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal < 1.0) {
     376          67 :         state.dataSize->DataDXCoolsLowSpeedsAutozize = true;
     377          67 :         state.dataSize->DataConstantUsedForSizing = -999.0;
     378          67 :         state.dataSize->DataFlowUsedForSizing = this->parentModeRatedEvapAirFlowRate;
     379          67 :         state.dataSize->DataFractionUsedForSizing = this->original_input_specs.gross_rated_total_cooling_capacity_ratio_to_nominal;
     380             :     }
     381         128 :     sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     382         128 :     this->rated_total_capacity = sizerCoolingCapacity.size(state, this->rated_total_capacity, errorsFound);
     383             : 
     384             :     //  DataSizing::DataEMSOverrideON = DXCoil( DXCoilNum ).RatedSHREMSOverrideOn( Mode );
     385             :     //  DataSizing::DataEMSOverride = DXCoil( DXCoilNum ).RatedSHREMSOverrideValue( Mode );
     386         128 :     state.dataSize->DataFlowUsedForSizing = this->evap_air_flow_rate;
     387         128 :     state.dataSize->DataCapacityUsedForSizing = this->rated_total_capacity;
     388         128 :     bool errorFound = false;
     389         128 :     CoolingSHRSizer sizerCoolingSHR;
     390         128 :     sizerCoolingSHR.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
     391         128 :     if (this->grossRatedSHR == DataSizing::AutoSize && this->parentOperatingMode == 2) {
     392           0 :         state.dataSize->DataSizingFraction = 0.667;
     393           0 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     394         128 :     } else if (this->grossRatedSHR == DataSizing::AutoSize && this->parentOperatingMode == 3) {
     395           0 :         state.dataSize->DataSizingFraction = 0.333;
     396           0 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     397             :     } else {
     398         128 :         this->grossRatedSHR = sizerCoolingSHR.size(state, this->grossRatedSHR, errorFound);
     399             :     }
     400         128 :     state.dataSize->DataFlowUsedForSizing = 0.0;
     401         128 :     state.dataSize->DataCapacityUsedForSizing = 0.0;
     402         128 :     state.dataSize->DataTotCapCurveIndex = 0;
     403         128 :     state.dataSize->DataSizingFraction = 1.0;
     404             :     //  DataSizing::DataEMSOverrideON = false;
     405             :     //  DataSizing::DataEMSOverride = 0.0;
     406             : 
     407         128 :     if (this->indexSHRFT > 0 && this->indexSHRFFF > 0) {
     408           3 :         this->RatedCBF = 0.001;
     409             :     } else {
     410             : 
     411         125 :         this->RatedCBF = CalcBypassFactor(state,
     412             :                                           RatedInletAirTemp,
     413             :                                           RatedInletAirHumRat,
     414             :                                           this->rated_total_capacity,
     415             :                                           this->grossRatedSHR,
     416             :                                           Psychrometrics::PsyHFnTdbW(RatedInletAirTemp, RatedInletAirHumRat),
     417             :                                           DataEnvironment::StdPressureSeaLevel);
     418             :     }
     419         128 :     this->RatedEIR = 1.0 / this->original_input_specs.gross_rated_cooling_COP;
     420         128 :     this->ratedLatentCapacity = this->rated_total_capacity * (1.0 - this->grossRatedSHR);
     421             : 
     422             :     // reset for next speed or coil
     423         128 :     state.dataSize->DataConstantUsedForSizing = 0.0;
     424         128 :     state.dataSize->DataDXCoolsLowSpeedsAutozize = false;
     425         128 : }
     426             : 
     427    11163381 : void CoilCoolingDXCurveFitSpeed::CalcSpeedOutput(EnergyPlus::EnergyPlusData &state,
     428             :                                                  const DataLoopNode::NodeData &inletNode,
     429             :                                                  DataLoopNode::NodeData &outletNode,
     430             :                                                  Real64 &_PLR,
     431             :                                                  HVAC::FanOp const fanOp,
     432             :                                                  const Real64 condInletTemp)
     433             : {
     434             : 
     435             :     // SUBROUTINE PARAMETER DEFINITIONS:
     436             :     static constexpr std::string_view RoutineName("CalcSpeedOutput: ");
     437             : 
     438    11163381 :     if ((_PLR == 0.0) || (AirMassFlow == 0.0)) {
     439     4870035 :         outletNode.Temp = inletNode.Temp;
     440     4870035 :         outletNode.HumRat = inletNode.HumRat;
     441     4870035 :         outletNode.Enthalpy = inletNode.Enthalpy;
     442     4870035 :         outletNode.Press = inletNode.Press;
     443     4870035 :         fullLoadPower = 0.0;
     444     4870035 :         fullLoadWasteHeat = 0.0;
     445     4870035 :         RTF = 0.0;
     446     4870035 :         return;
     447             :     }
     448             : 
     449             :     Real64 hDelta; // enthalpy difference across cooling coil
     450             :     Real64 A0;     // ratio of UA to Cp
     451             :     Real64 CBF;    // adjusted coil bypass factor
     452     6293346 :     if (RatedCBF > 0.0) {
     453     6293346 :         A0 = -std::log(RatedCBF) * RatedAirMassFlowRate;
     454             :     } else {
     455             :         // This is bad - results in CBF = 1.0 which results in divide by zero below: hADP = inletState.h - hDelta / (1.0 - CBF)
     456           0 :         ShowFatalError(state, format("{}Rated CBF={:.6R} is <= 0.0 for {}={}", RoutineName, RatedCBF, object_name, name));
     457           0 :         A0 = 0.0;
     458             :     }
     459     6293346 :     Real64 ADiff = -A0 / AirMassFlow;
     460     6293346 :     if (ADiff >= DataPrecisionGlobals::EXP_LowerLimit) {
     461     6284889 :         CBF = std::exp(ADiff);
     462             :     } else {
     463        8457 :         CBF = 0.0;
     464             :     }
     465             : 
     466     6293346 :     assert(ambPressure > 0.0);
     467     6293346 :     Real64 inletWetBulb = Psychrometrics::PsyTwbFnTdbWPb(state, inletNode.Temp, inletNode.HumRat, ambPressure);
     468     6293346 :     Real64 inletw = inletNode.HumRat;
     469             : 
     470     6293346 :     int Counter = 0;                  // iteration counter for dry coil condition
     471     6293346 :     int constexpr MaxIter(30);        // iteration limit
     472     6293346 :     Real64 constexpr Tolerance(0.01); // iteration convergence limit
     473     6293346 :     Real64 RF = 0.4;                  // relaxation factor for holding back changes in value during iteration
     474             :     Real64 TotCap;
     475             :     Real64 SHR;
     476             :     while (true) {
     477             : 
     478     6524381 :         Real64 TotCapTempModFac = 1.0;
     479     6524381 :         if (indexCapFT > 0) {
     480     6524381 :             if (state.dataCurveManager->PerfCurve(indexCapFT)->numDims == 2) {
     481     6524381 :                 TotCapTempModFac = Curve::CurveValue(state, indexCapFT, inletWetBulb, condInletTemp);
     482             :             } else {
     483           0 :                 TotCapTempModFac = Curve::CurveValue(state, indexCapFT, condInletTemp);
     484             :             }
     485             :         }
     486     6524381 :         Real64 TotCapFlowModFac = 1.0;
     487     6524381 :         if (indexCapFFF > 0) {
     488     6524381 :             TotCapFlowModFac = Curve::CurveValue(state, indexCapFFF, AirFF);
     489             :         }
     490             : 
     491     6524381 :         TotCap = this->rated_total_capacity * TotCapFlowModFac * TotCapTempModFac;
     492     6524381 :         hDelta = TotCap / AirMassFlow;
     493             : 
     494     6524381 :         if (indexSHRFT > 0 && indexSHRFFF > 0) {
     495       36461 :             Real64 SHRTempModFrac = max(Curve::CurveValue(state, indexSHRFT, inletWetBulb, inletNode.Temp), 0.0);
     496             : 
     497       36461 :             Real64 SHRFlowModFrac = max(Curve::CurveValue(state, indexSHRFFF, AirFF), 0.0);
     498             : 
     499       36461 :             SHR = this->grossRatedSHR * SHRTempModFrac * SHRFlowModFrac;
     500       36461 :             SHR = max(min(SHR, 1.0), 0.0);
     501       36461 :             break;
     502             :         } else {
     503             :             // Calculate apparatus dew point conditions using TotCap and CBF
     504     6487920 :             Real64 hADP = inletNode.Enthalpy - hDelta / (1.0 - CBF);
     505     6487920 :             Real64 tADP = Psychrometrics::PsyTsatFnHPb(state, hADP, ambPressure, RoutineName);
     506     6487920 :             Real64 wADP = Psychrometrics::PsyWFnTdbH(state, tADP, hADP, RoutineName);
     507     6487920 :             Real64 hTinwADP = Psychrometrics::PsyHFnTdbW(inletNode.Temp, wADP);
     508     6487920 :             if ((inletNode.Enthalpy - hADP) > 1.e-10) {
     509     6487920 :                 SHR = min((hTinwADP - hADP) / (inletNode.Enthalpy - hADP), 1.0);
     510             :             } else {
     511           0 :                 SHR = 1.0;
     512             :             }
     513             :             // Check for dry evaporator conditions (win < wadp)
     514     6487920 :             if (wADP > inletw || (Counter >= 1 && Counter < MaxIter)) {
     515      295842 :                 if (inletw == 0.0) inletw = 0.00001;
     516      295842 :                 Real64 werror = (inletw - wADP) / inletw;
     517             :                 // Increase InletAirHumRatTemp at constant InletAirTemp to find coil dry-out point. Then use the
     518             :                 // capacity at the dry-out point to determine exiting conditions from coil. This is required
     519             :                 // since the TotCapTempModFac doesn't work properly with dry-coil conditions.
     520      295842 :                 inletw = RF * wADP + (1.0 - RF) * inletw;
     521      295842 :                 inletWetBulb = Psychrometrics::PsyTwbFnTdbWPb(state, inletNode.Temp, inletw, ambPressure);
     522      295842 :                 ++Counter;
     523      295842 :                 if (std::abs(werror) > Tolerance) continue; // Recalculate with modified inlet conditions
     524       64807 :                 break;
     525             :             } else {
     526             :                 break;
     527             :             }
     528             :         }
     529      231035 :     }
     530             : 
     531     6293346 :     assert(SHR >= 0.0);
     532             : 
     533     6293346 :     Real64 PLF = 1.0; // part load factor as a function of PLR, RTF = PLR / PLF
     534     6293346 :     if (indexPLRFPLF > 0) {
     535     6293346 :         PLF = Curve::CurveValue(state, indexPLRFPLF, _PLR); // Calculate part-load factor
     536             :     }
     537     6293346 :     if (fanOp == HVAC::FanOp::Cycling) state.dataHVACGlobal->OnOffFanPartLoadFraction = PLF;
     538             : 
     539     6293346 :     Real64 EIRTempModFac = 1.0; // EIR as a function of temperature curve result
     540     6293346 :     if (indexEIRFT > 0) {
     541     6293346 :         if (state.dataCurveManager->PerfCurve(indexEIRFT)->numDims == 2) {
     542     6293346 :             EIRTempModFac = Curve::CurveValue(state, indexEIRFT, inletWetBulb, condInletTemp);
     543             :         } else {
     544           0 :             EIRTempModFac = Curve::CurveValue(state, indexEIRFT, condInletTemp);
     545             :         }
     546             :     }
     547     6293346 :     Real64 EIRFlowModFac = 1.0; // EIR as a function of flow fraction curve result
     548     6293346 :     if (indexEIRFFF > 0) {
     549     6293346 :         EIRFlowModFac = Curve::CurveValue(state, indexEIRFFF, AirFF);
     550             :     }
     551             : 
     552     6293346 :     Real64 wasteHeatTempModFac = 1.0; // waste heat fraction as a function of temperature curve result
     553     6293346 :     if (indexWHFT > 0) {
     554     4523469 :         wasteHeatTempModFac = Curve::CurveValue(state, indexWHFT, condInletTemp, inletNode.Temp);
     555             :     }
     556             : 
     557     6293346 :     Real64 EIR = RatedEIR * EIRFlowModFac * EIRTempModFac;
     558     6293346 :     RTF = _PLR / PLF;
     559     6293346 :     fullLoadPower = TotCap * EIR;
     560     6293346 :     fullLoadWasteHeat = ratedWasteHeatFractionOfPowerInput * wasteHeatTempModFac * fullLoadPower;
     561             : 
     562     6293346 :     outletNode.Enthalpy = inletNode.Enthalpy - hDelta;
     563     6293346 :     Real64 hTinwout = inletNode.Enthalpy - ((1.0 - SHR) * hDelta);
     564     6293346 :     outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, inletNode.Temp, hTinwout);
     565     6293346 :     outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
     566             : 
     567             :     //  If constant fan with cycling compressor, call function to determine "effective SHR"
     568             :     //  which includes the part-load degradation on latent capacity
     569     6293346 :     if (this->doLatentDegradation && (fanOp == HVAC::FanOp::Continuous)) {
     570       20053 :         Real64 QLatActual = TotCap * (1.0 - SHR);
     571             :         // TODO: Figure out HeatingRTF for this
     572       20053 :         Real64 HeatingRTF = 0.0;
     573       20053 :         SHR = calcEffectiveSHR(inletNode, inletWetBulb, SHR, RTF, ratedLatentCapacity, QLatActual, HeatingRTF);
     574             :         // Calculate full load output conditions
     575       20053 :         if (SHR > 1.0) SHR = 1.0;
     576       20053 :         hTinwout = inletNode.Enthalpy - (1.0 - SHR) * hDelta;
     577       20053 :         if (SHR < 1.0) {
     578       11101 :             outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, inletNode.Temp, hTinwout, RoutineName);
     579             :         } else {
     580        8952 :             outletNode.HumRat = inletNode.HumRat;
     581             :         }
     582       20053 :         outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
     583             :     }
     584             :     // Check for saturation error and modify temperature at constant enthalpy
     585     6293346 :     Real64 tsat = Psychrometrics::PsyTsatFnHPb(state, outletNode.Enthalpy, inletNode.Press, RoutineName);
     586     6293346 :     if (outletNode.Temp < tsat) {
     587      601806 :         outletNode.Temp = tsat;
     588      601806 :         outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, tsat, outletNode.Enthalpy);
     589             :     }
     590             : }
     591             : 
     592         125 : Real64 CoilCoolingDXCurveFitSpeed::CalcBypassFactor(EnergyPlus::EnergyPlusData &state,
     593             :                                                     Real64 const tdb, // Inlet dry-bulb temperature {C}
     594             :                                                     Real64 const w,   // Inlet humidity ratio {kg-H2O/kg-dryair}
     595             :                                                     Real64 const q,   // Total capacity {W}
     596             :                                                     Real64 const shr, // SHR
     597             :                                                     Real64 const h,   // Inlet enthalpy {J/kg-dryair}
     598             :                                                     Real64 const p)   // Outlet node pressure {Pa}
     599             : {
     600             : 
     601             :     static constexpr std::string_view RoutineName("CalcBypassFactor: ");
     602         125 :     Real64 constexpr SmallDifferenceTest(0.00000001);
     603             : 
     604             :     // Bypass factors are calculated at rated conditions at sea level (make sure in.p is Standard Pressure)
     605             :     Real64 calcCBF;
     606             : 
     607         125 :     Real64 airMassFlowRate = this->evap_air_flow_rate * Psychrometrics::PsyRhoAirFnPbTdbW(state, p, tdb, w);
     608         125 :     Real64 deltaH = q / airMassFlowRate;
     609         125 :     Real64 outp = p;
     610         125 :     Real64 outh = h - deltaH;
     611         125 :     Real64 outw = Psychrometrics::PsyWFnTdbH(state, tdb, h - (1.0 - shr) * deltaH); // enthalpy at Tdb,in and Wout
     612         125 :     Real64 outtdb = Psychrometrics::PsyTdbFnHW(outh, outw);
     613         125 :     Real64 outrh = Psychrometrics::PsyRhFnTdbWPb(state, outtdb, outw, outp);
     614             : 
     615         125 :     if (outrh >= 1.0) {
     616           0 :         ShowWarningError(state, std::string{RoutineName} + ": For object = " + this->object_name + ", name = \"" + this->name + "\"");
     617           0 :         ShowContinueError(state, "Calculated outlet air relative humidity greater than 1. The combination of");
     618           0 :         ShowContinueError(state, "rated air volume flow rate, total cooling capacity and sensible heat ratio yields coil exiting");
     619           0 :         ShowContinueError(state, "air conditions above the saturation curve. Possible fixes are to reduce the rated total cooling");
     620           0 :         ShowContinueError(state, "capacity, increase the rated air volume flow rate, or reduce the rated sensible heat ratio for this coil.");
     621           0 :         ShowContinueError(state, "If autosizing, it is recommended that all three of these values be autosized.");
     622           0 :         ShowContinueError(state, "...Inputs used for calculating cooling coil bypass factor.");
     623           0 :         ShowContinueError(state, format("...Inlet Air Temperature     = {:.2R} C", tdb));
     624           0 :         ShowContinueError(state, format("...Outlet Air Temperature    = {:.2R} C", outtdb));
     625           0 :         ShowContinueError(state, format("...Inlet Air Humidity Ratio  = {:.6R} kgWater/kgDryAir", w));
     626           0 :         ShowContinueError(state, format("...Outlet Air Humidity Ratio = {:.6R} kgWater/kgDryAir", outw));
     627           0 :         ShowContinueError(state, format("...Total Cooling Capacity used in calculation = {:.2R} W", q));
     628           0 :         ShowContinueError(state, format("...Air Mass Flow Rate used in calculation     = {:.6R} kg/s", airMassFlowRate));
     629           0 :         ShowContinueError(state, format("...Air Volume Flow Rate used in calculation   = {:.6R} m3/s", this->evap_air_flow_rate));
     630           0 :         if (q > 0.0) {
     631           0 :             if (((this->minRatedVolFlowPerRatedTotCap - this->evap_air_flow_rate / q) > SmallDifferenceTest) ||
     632           0 :                 ((this->evap_air_flow_rate / q - this->maxRatedVolFlowPerRatedTotCap) > SmallDifferenceTest)) {
     633           0 :                 ShowContinueError(state,
     634           0 :                                   format("...Air Volume Flow Rate per Watt of Rated Cooling Capacity is also out of bounds at = {:.7R} m3/s/W",
     635           0 :                                          this->evap_air_flow_rate / q));
     636             :             }
     637             :         }
     638           0 :         Real64 outletAirTempSat = Psychrometrics::PsyTsatFnHPb(state, outh, outp, RoutineName);
     639           0 :         if (outtdb < outletAirTempSat) { // Limit to saturated conditions at OutletAirEnthalpy
     640           0 :             outtdb = outletAirTempSat + 0.005;
     641           0 :             outw = Psychrometrics::PsyWFnTdbH(state, outtdb, outh, RoutineName);
     642           0 :             Real64 adjustedSHR = (Psychrometrics::PsyHFnTdbW(tdb, outw) - outh) / deltaH;
     643           0 :             ShowWarningError(state,
     644           0 :                              std::string{RoutineName} + object_name + " \"" + name +
     645             :                                  "\", SHR adjusted to achieve valid outlet air properties and the simulation continues.");
     646           0 :             ShowContinueError(state, format("Initial SHR = {:.5R}", this->grossRatedSHR));
     647           0 :             ShowContinueError(state, format("Adjusted SHR = {:.5R}", adjustedSHR));
     648             :         }
     649             :     }
     650             : 
     651             :     // ADP conditions
     652         125 :     Real64 adp_tdb = Psychrometrics::PsyTdpFnWPb(state, outw, outp);
     653             : 
     654         125 :     Real64 deltaT = tdb - outtdb;
     655         125 :     Real64 deltaHumRat = w - outw;
     656         125 :     Real64 slopeAtConds = 0.0;
     657         125 :     if (deltaT > 0.0) slopeAtConds = deltaHumRat / deltaT;
     658         125 :     if (slopeAtConds <= 0.0) {
     659           0 :         ShowSevereError(state, this->object_name + " \"" + this->name + "\"");
     660           0 :         ShowContinueError(state, "...Invalid slope or outlet air condition when calculating cooling coil bypass factor.");
     661           0 :         ShowContinueError(state, format("...Slope = {:.8R}", slopeAtConds));
     662           0 :         ShowContinueError(state, format("...Inlet Air Temperature     = {:.2R} C", tdb));
     663           0 :         ShowContinueError(state, format("...Outlet Air Temperature    = {:.2R} C", outtdb));
     664           0 :         ShowContinueError(state, format("...Inlet Air Humidity Ratio  = {:.6R} kgWater/kgDryAir", w));
     665           0 :         ShowContinueError(state, format("...Outlet Air Humidity Ratio = {:.6R} kgWater/kgDryAir", outw));
     666           0 :         ShowContinueError(state, format("...Total Cooling Capacity used in calculation = {:.2R} W", q));
     667           0 :         ShowContinueError(state, format("...Air Mass Flow Rate used in calculation     = {:.6R} kg/s", airMassFlowRate));
     668           0 :         ShowContinueError(state, format("...Air Volume Flow Rate used in calculation   = {:.6R} m3/s", this->evap_air_flow_rate));
     669           0 :         if (q > 0.0) {
     670           0 :             if (((this->minRatedVolFlowPerRatedTotCap - this->evap_air_flow_rate / q) > SmallDifferenceTest) ||
     671           0 :                 ((this->evap_air_flow_rate / q - this->maxRatedVolFlowPerRatedTotCap) > SmallDifferenceTest)) {
     672           0 :                 ShowContinueError(state,
     673           0 :                                   format("...Air Volume Flow Rate per Watt of Rated Cooling Capacity is also out of bounds at = {:.7R} m3/s/W",
     674           0 :                                          this->evap_air_flow_rate / q));
     675             :             }
     676             :         }
     677           0 :         ShowFatalError(state, "Errors found in calculating coil bypass factors");
     678             :     }
     679             : 
     680         125 :     Real64 adp_w = min(outw, Psychrometrics::PsyWFnTdpPb(state, adp_tdb, DataEnvironment::StdPressureSeaLevel));
     681             : 
     682         125 :     int iter = 0;
     683         125 :     int constexpr maxIter(50);
     684         125 :     Real64 errorLast = 100.0;
     685         125 :     Real64 deltaADPTemp = 5.0;
     686         125 :     Real64 tolerance = 1.0; // initial conditions for iteration
     687         125 :     bool cbfErrors = false;
     688        1821 :     while ((iter <= maxIter) && (tolerance > 0.001)) {
     689             : 
     690             :         // Do for IterMax iterations or until the error gets below .1%
     691        1696 :         if (iter > 0) adp_tdb += deltaADPTemp;
     692        1696 :         ++iter;
     693             :         //  Find new slope using guessed Tadp
     694        1696 :         adp_w = min(outw, Psychrometrics::PsyWFnTdpPb(state, adp_tdb, DataEnvironment::StdPressureSeaLevel));
     695        1696 :         Real64 slope = (w - adp_w) / max(0.001, (tdb - adp_tdb));
     696             :         //  check for convergence (slopes are equal to within error tolerance)
     697        1696 :         Real64 error = (slope - slopeAtConds) / slopeAtConds;
     698        1696 :         if ((error > 0.0) && (errorLast < 0.0)) {
     699         511 :             deltaADPTemp = -deltaADPTemp / 2.0;
     700        1185 :         } else if ((error < 0.0) && (errorLast > 0.0)) {
     701         609 :             deltaADPTemp = -deltaADPTemp / 2.0;
     702         576 :         } else if (std::abs(error) > std::abs(errorLast)) {
     703           0 :             deltaADPTemp = -deltaADPTemp / 2.0;
     704             :         }
     705        1696 :         errorLast = error;
     706        1696 :         tolerance = std::abs(error);
     707             :     }
     708             : 
     709             :     //   Calculate Bypass Factor from Enthalpies
     710         125 :     Real64 adp_h = Psychrometrics::PsyHFnTdbW(adp_tdb, adp_w);
     711         125 :     calcCBF = min(1.0, (outh - adp_h) / (h - adp_h));
     712             : 
     713         125 :     if (iter > maxIter) {
     714           0 :         ShowSevereError(state,
     715           0 :                         std::string{RoutineName} + object_name + " \"" + name +
     716             :                             "\" -- coil bypass factor calculation did not converge after max iterations.");
     717           0 :         ShowContinueError(state, format("The RatedSHR of [{:.3R}], entered by the user or autosized (see *.eio file),", this->grossRatedSHR));
     718           0 :         ShowContinueError(state, "may be causing this. The line defined by the coil rated inlet air conditions");
     719           0 :         ShowContinueError(state, "(26.7C drybulb and 19.4C wetbulb) and the RatedSHR (i.e., slope of the line) must intersect");
     720           0 :         ShowContinueError(state, "the saturation curve of the psychrometric chart. If the RatedSHR is too low, then this");
     721           0 :         ShowContinueError(state, "intersection may not occur and the coil bypass factor calculation will not converge.");
     722           0 :         ShowContinueError(state, "If autosizing the SHR, recheck the design supply air humidity ratio and design supply air");
     723           0 :         ShowContinueError(state, "temperature values in the Sizing:System and Sizing:Zone objects. In general, the temperatures");
     724           0 :         ShowContinueError(state, "and humidity ratios specified in these two objects should be the same for each system");
     725           0 :         ShowContinueError(state, "and the zones that it serves.");
     726           0 :         ShowContinueErrorTimeStamp(state, "");
     727           0 :         cbfErrors = true; // Didn't converge within MaxIter iterations
     728             :     }
     729         125 :     if (calcCBF < 0.0) {
     730           0 :         ShowSevereError(state, std::string{RoutineName} + object_name + " \"" + name + "\" -- negative coil bypass factor calculated.");
     731           0 :         ShowContinueErrorTimeStamp(state, "");
     732           0 :         cbfErrors = true; // Negative CBF not valid
     733             :     }
     734             :     // Show fatal error for specific coil that caused a CBF error
     735         125 :     if (cbfErrors) {
     736           0 :         ShowFatalError(state, std::string{RoutineName} + object_name + " \"" + name + "\" Errors found in calculating coil bypass factors");
     737             :     }
     738         125 :     return calcCBF;
     739             : }
     740             : 
     741       20053 : Real64 CoilCoolingDXCurveFitSpeed::calcEffectiveSHR(const DataLoopNode::NodeData &inletNode,
     742             :                                                     Real64 const inletWetBulb,
     743             :                                                     Real64 const SHRss,      // Steady-state sensible heat ratio
     744             :                                                     Real64 const RTF,        // Compressor run-time fraction
     745             :                                                     Real64 const QLatRated,  // Rated latent capacity
     746             :                                                     Real64 const QLatActual, // Actual latent capacity
     747             :                                                     Real64 const HeatingRTF  // Used to recalculate Toff for cycling fan systems
     748             : )
     749             : {
     750             :     // PURPOSE OF THIS FUNCTION:
     751             :     //    Adjust sensible heat ratio to account for degradation of DX coil latent
     752             :     //    capacity at part-load (cycling) conditions.
     753             : 
     754             :     // METHODOLOGY EMPLOYED:
     755             :     //    With model parameters entered by the user, the part-load latent performance
     756             :     //    of a DX cooling coil is determined for a constant air flow system with
     757             :     //    a cooling coil that cycles on/off. The model calculates the time
     758             :     //    required for condensate to begin falling from the cooling coil.
     759             :     //    Runtimes greater than this are integrated to a "part-load" latent
     760             :     //    capacity which is used to determine the "part-load" sensible heat ratio.
     761             :     //    See reference below for additional details (linear decay model, Eq. 8b).
     762             :     // REFERENCES:
     763             :     //   "A Model to Predict the Latent Capacity of Air Conditioners and
     764             :     //    Heat Pumps at Part-Load Conditions with Constant Fan Operation"
     765             :     //    1996 ASHRAE Transactions, Volume 102, Part 1, Pp. 266 - 274,
     766             :     //    Hugh I. Henderson, Jr., P.E., Kannan Rengarajan, P.E.
     767             : 
     768             :     // Return value
     769             :     Real64 SHReff; // Effective sensible heat ratio, includes degradation due to cycling effects
     770             : 
     771             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     772             :     Real64 Twet; // Nominal time for condensate to begin leaving the coil's condensate drain line
     773             :     //   at the current operating conditions (sec)
     774             :     Real64 Gamma; // Initial moisture evaporation rate divided by steady-state AC latent capacity
     775             :     //   at the current operating conditions
     776             :     Real64 Twet_max; // Maximum allowed value for Twet
     777             :     Real64 Ton;      // Coil on time (sec)
     778             :     Real64 Toff;     // Coil off time (sec)
     779             :     Real64 Toffa;    // Actual coil off time (sec). Equations valid for Toff <= (2.0 * Twet/Gamma)
     780             :     Real64 aa;       // Intermediate variable
     781             :     Real64 To1;      // Intermediate variable (first guess at To). To = time to the start of moisture removal
     782             :     Real64 To2;      // Intermediate variable (second guess at To). To = time to the start of moisture removal
     783             :     Real64 Error;    // Error for iteration (DO) loop
     784             :     Real64 LHRmult;  // Latent Heat Ratio (LHR) multiplier. The effective latent heat ratio LHR = (1-SHRss)*LHRmult
     785             :     Real64 Ton_heating;
     786             :     Real64 Toff_heating;
     787             : 
     788       20053 :     Real64 Twet_Rated = parentModeTimeForCondensateRemoval; // Time wet at rated conditions (sec)
     789       20053 :     Real64 Gamma_Rated = parentModeEvapRateRatio;           // Gamma at rated conditions
     790       20053 :     Real64 Nmax = parentModeMaxCyclingRate;                 // Maximum ON/OFF cycles for the compressor (cycles/hr)
     791       20053 :     Real64 Tcl = parentModeLatentTimeConst;                 // Time constant for latent capacity to reach steady state after startup(sec)
     792             : 
     793             :     //  No moisture evaporation (latent degradation) occurs for runtime fraction of 1.0
     794             :     //  All latent degradation model parameters cause divide by 0.0 if not greater than 0.0
     795             :     //  Latent degradation model parameters initialize to 0.0 meaning no evaporation model used.
     796       20053 :     if (RTF >= 1.0) {
     797       10199 :         SHReff = SHRss;
     798       10199 :         return SHReff;
     799             :     }
     800             : 
     801        9854 :     Twet_max = 9999.0; // high limit for Twet
     802             : 
     803             :     //  Calculate the model parameters at the actual operating conditions
     804        9854 :     Twet = min(Twet_Rated * QLatRated / (QLatActual + 1.e-10), Twet_max);
     805        9854 :     Gamma = Gamma_Rated * QLatRated * (inletNode.Temp - inletWetBulb) / ((26.7 - 19.4) * QLatActual + 1.e-10);
     806             : 
     807             :     //  Calculate the compressor on and off times using a converntional thermostat curve
     808        9854 :     Ton = 3600.0 / (4.0 * Nmax * (1.0 - RTF)); // duration of cooling coil on-cycle (sec)
     809        9854 :     Toff = 3600.0 / (4.0 * Nmax * RTF);        // duration of cooling coil off-cycle (sec)
     810             : 
     811             :     //  Cap Toff to meet the equation restriction
     812        9854 :     if (Gamma > 0.0) {
     813        9854 :         Toffa = min(Toff, 2.0 * Twet / Gamma);
     814             :     } else {
     815           0 :         Toffa = Toff;
     816             :     }
     817             : 
     818             :     //  Need to include the reheat coil operation to account for actual fan run time. E+ uses a
     819             :     //  separate heating coil for heating and reheat (to separate the heating and reheat loads)
     820             :     //  and real world applications would use a single heating coil for both purposes, the actual
     821             :     //  fan operation is based on HeatingPLR + ReheatPLR. For cycling fan RH control, latent
     822             :     //  degradation only occurs when a heating load exists, in this case the reheat load is
     823             :     //  equal to and oposite in magnitude to the cooling coil sensible output but the reheat
     824             :     //  coil is not always active. This additional fan run time has not been accounted for at this time.
     825             :     //  Recalculate Toff for cycling fan systems when heating is active
     826        9854 :     if (HeatingRTF > 0.0) {
     827           0 :         if (HeatingRTF < 1.0 && HeatingRTF > RTF) {
     828           0 :             Ton_heating = 3600.0 / (4.0 * Nmax * (1.0 - HeatingRTF));
     829           0 :             Toff_heating = 3600.0 / (4.0 * Nmax * HeatingRTF);
     830             :             //    add additional heating coil operation during cooling coil off cycle (due to cycling rate difference of coils)
     831           0 :             Ton_heating += max(0.0, min(Ton_heating, (Ton + Toffa) - (Ton_heating + Toff_heating)));
     832           0 :             Toffa = min(Toffa, Ton_heating - Ton);
     833             :         }
     834             :     }
     835             : 
     836             :     //  Use sucessive substitution to solve for To
     837        9854 :     aa = (Gamma * Toffa) - (0.25 / Twet) * pow_2(Gamma) * pow_2(Toffa);
     838        9854 :     To1 = aa + Tcl;
     839        9854 :     Error = 1.0;
     840       25217 :     while (Error > 0.001) {
     841       15363 :         To2 = aa - Tcl * (std::exp(-To1 / Tcl) - 1.0);
     842       15363 :         Error = std::abs((To2 - To1) / To1);
     843       15363 :         To1 = To2;
     844             :     }
     845             : 
     846             :     //  Adjust Sensible Heat Ratio (SHR) using Latent Heat Ratio (LHR) multiplier
     847             :     //  Floating underflow errors occur when -Ton/Tcl is a large negative number.
     848             :     //  Cap lower limit at -700 to avoid the underflow errors.
     849        9854 :     aa = std::exp(max(-700.0, -Ton / Tcl));
     850             :     //  Calculate latent heat ratio multiplier
     851        9854 :     LHRmult = max(((Ton - To2) / (Ton + Tcl * (aa - 1.0))), 0.0);
     852             : 
     853             :     //  Calculate part-load or "effective" sensible heat ratio
     854        9854 :     SHReff = 1.0 - (1.0 - SHRss) * LHRmult;
     855             : 
     856        9854 :     if (SHReff < SHRss) SHReff = SHRss; // Effective SHR can be less than the steady-state SHR
     857        9854 :     if (SHReff > 1.0) SHReff = 1.0;     // Effective sensible heat ratio can't be greater than 1.0
     858             : 
     859        9854 :     return SHReff;
     860             : }

Generated by: LCOV version 1.14