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