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 : }
|