LCOV - code coverage report
Current view: top level - EnergyPlus/Coils - CoilCoolingDXCurveFitSpeed.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 77.3 % 462 357
Test Date: 2025-06-03 15:18:44 Functions: 100.0 % 7 7

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

Generated by: LCOV version 2.0-1