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 <EnergyPlus/Coils/CoilCoolingDXCurveFitPerformance.hh>
49 : #include <EnergyPlus/CurveManager.hh>
50 : #include <EnergyPlus/Data/EnergyPlusData.hh>
51 : #include <EnergyPlus/DataEnvironment.hh>
52 : #include <EnergyPlus/DataGlobalConstants.hh>
53 : #include <EnergyPlus/DataHVACGlobals.hh>
54 : #include <EnergyPlus/DataIPShortCuts.hh>
55 : #include <EnergyPlus/Fans.hh>
56 : #include <EnergyPlus/General.hh>
57 : #include <EnergyPlus/GeneralRoutines.hh>
58 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
59 : #include <EnergyPlus/OutputReportPredefined.hh>
60 : #include <EnergyPlus/Psychrometrics.hh>
61 : #include <EnergyPlus/ScheduleManager.hh>
62 : #include <EnergyPlus/UtilityRoutines.hh>
63 :
64 : using namespace EnergyPlus;
65 :
66 54 : void CoilCoolingDXCurveFitPerformance::instantiateFromInputSpec(EnergyPlus::EnergyPlusData &state,
67 : const CoilCoolingDXCurveFitPerformanceInputSpecification &input_data)
68 : {
69 : static constexpr std::string_view routineName("CoilCoolingDXCurveFitPerformance::instantiateFromInputSpec: ");
70 :
71 54 : ErrorObjectHeader eoh{routineName, this->object_name, input_data.name};
72 :
73 54 : bool errorsFound(false);
74 54 : this->original_input_specs = input_data;
75 54 : this->name = input_data.name;
76 54 : this->minOutdoorDrybulb = input_data.minimum_outdoor_dry_bulb_temperature_for_compressor_operation;
77 54 : this->maxOutdoorDrybulbForBasin = input_data.maximum_outdoor_dry_bulb_temperature_for_crankcase_heater_operation;
78 54 : this->crankcaseHeaterCap = input_data.crankcase_heater_capacity;
79 54 : this->normalMode = CoilCoolingDXCurveFitOperatingMode(state, input_data.base_operating_mode_name);
80 54 : this->normalMode.oneTimeInit(state); // oneTimeInit does not need to be delayed in this use case
81 :
82 54 : if (Util::SameString(input_data.capacity_control, "CONTINUOUS")) {
83 2 : this->capControlMethod = CapControlMethod::CONTINUOUS;
84 52 : } else if (Util::SameString(input_data.capacity_control, "DISCRETE")) {
85 52 : this->capControlMethod = CapControlMethod::DISCRETE;
86 : } else {
87 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", routineName, this->object_name, this->name));
88 0 : ShowContinueError(state, format("...Capacity Control Method=\"{}\":", input_data.capacity_control));
89 0 : ShowContinueError(state, "...must be Discrete or Continuous.");
90 0 : errorsFound = true;
91 : }
92 54 : this->evapCondBasinHeatCap = input_data.basin_heater_capacity;
93 54 : this->evapCondBasinHeatSetpoint = input_data.basin_heater_setpoint_temperature;
94 54 : if (input_data.basin_heater_operating_schedule_name.empty()) {
95 54 : this->evapCondBasinHeatSched = Sched::GetScheduleAlwaysOn(state);
96 0 : } else if ((this->evapCondBasinHeatSched = Sched::GetSchedule(state, input_data.basin_heater_operating_schedule_name)) == nullptr) {
97 0 : ShowSevereItemNotFound(
98 : state, eoh, "Evaporative Condenser Basin Heater Operating Schedule Name", input_data.basin_heater_operating_schedule_name);
99 0 : errorsFound = true;
100 : }
101 :
102 54 : if (!input_data.alternate_operating_mode_name.empty() && input_data.alternate_operating_mode2_name.empty()) {
103 5 : this->maxAvailCoilMode = HVAC::CoilMode::Enhanced;
104 5 : this->alternateMode = CoilCoolingDXCurveFitOperatingMode(state, input_data.alternate_operating_mode_name);
105 5 : this->alternateMode.oneTimeInit(state); // oneTimeInit does not need to be delayed in this use case
106 : }
107 : // Validate fuel type input
108 54 : this->compressorFuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, Util::makeUPPER(input_data.compressor_fuel_type)));
109 54 : if (this->compressorFuelType == Constant::eFuel::Invalid) {
110 0 : ShowSevereError(state, format("{} {} =\"{}\" invalid", std::string{routineName}, this->object_name, this->name));
111 0 : ShowContinueError(state, format("...Compressor Fuel Type=\"{}\".", input_data.compressor_fuel_type));
112 0 : errorsFound = true;
113 : }
114 :
115 54 : if (!input_data.alternate_operating_mode2_name.empty() && !input_data.alternate_operating_mode_name.empty()) {
116 1 : this->maxAvailCoilMode = HVAC::CoilMode::SubcoolReheat;
117 1 : this->alternateMode = CoilCoolingDXCurveFitOperatingMode(state, input_data.alternate_operating_mode_name);
118 1 : this->alternateMode2 = CoilCoolingDXCurveFitOperatingMode(state, input_data.alternate_operating_mode2_name);
119 1 : setOperMode(state, this->normalMode, 1);
120 1 : setOperMode(state, this->alternateMode, 2);
121 1 : setOperMode(state, this->alternateMode2, 3);
122 : }
123 :
124 54 : if (!input_data.outdoor_temperature_dependent_crankcase_heater_capacity_curve_name.empty()) {
125 0 : this->crankcaseHeaterCapacityCurveIndex =
126 0 : Curve::GetCurveIndex(state, input_data.outdoor_temperature_dependent_crankcase_heater_capacity_curve_name);
127 0 : if (this->crankcaseHeaterCapacityCurveIndex == 0) { // can't find the curve
128 0 : ShowSevereError(state,
129 0 : format("{} = {}: {} not found = {}",
130 : this->object_name,
131 0 : this->name,
132 : "Crankcase Heater Capacity Function of Temperature Curve Name",
133 0 : input_data.outdoor_temperature_dependent_crankcase_heater_capacity_curve_name));
134 :
135 0 : errorsFound = true;
136 : } else {
137 : // Verify Curve Object, only legal type is Quadratic and Cubic
138 0 : errorsFound |= Curve::CheckCurveDims(state,
139 : this->crankcaseHeaterCapacityCurveIndex, // Curve index
140 : {1}, // Valid dimensions
141 : routineName, // Routine name
142 : this->object_name, // Object Type
143 : this->name, // Object Name
144 : input_data.outdoor_temperature_dependent_crankcase_heater_capacity_curve_name); // Field Name
145 : }
146 : }
147 54 : if (errorsFound) {
148 0 : ShowFatalError(
149 : state,
150 0 : format("{} Errors found in getting {} input. Preceding condition(s) causes termination.", std::string{routineName}, this->object_name));
151 : }
152 54 : }
153 :
154 54 : CoilCoolingDXCurveFitPerformance::CoilCoolingDXCurveFitPerformance(EnergyPlus::EnergyPlusData &state, const std::string &name_to_find)
155 54 : : CoilCoolingDXPerformanceBase()
156 : {
157 54 : int numPerformances = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CoilCoolingDXCurveFitPerformance::object_name);
158 : if (numPerformances <= 0) {
159 : // error
160 : }
161 54 : bool found_it = false;
162 137 : for (int perfNum = 1; perfNum <= numPerformances; ++perfNum) {
163 : int NumAlphas; // Number of Alphas for each GetObjectItem call
164 : int NumNumbers; // Number of Numbers for each GetObjectItem call
165 : int IOStatus;
166 411 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
167 : CoilCoolingDXCurveFitPerformance::object_name,
168 : perfNum,
169 137 : state.dataIPShortCut->cAlphaArgs,
170 : NumAlphas,
171 137 : state.dataIPShortCut->rNumericArgs,
172 : NumNumbers,
173 : IOStatus,
174 : _,
175 137 : state.dataIPShortCut->lAlphaFieldBlanks);
176 137 : if (!Util::SameString(name_to_find, state.dataIPShortCut->cAlphaArgs(1))) {
177 83 : continue;
178 : }
179 54 : found_it = true;
180 :
181 54 : CoilCoolingDXCurveFitPerformanceInputSpecification input_specs;
182 :
183 54 : input_specs.name = state.dataIPShortCut->cAlphaArgs(1);
184 54 : input_specs.crankcase_heater_capacity = state.dataIPShortCut->rNumericArgs(1);
185 54 : input_specs.minimum_outdoor_dry_bulb_temperature_for_compressor_operation = state.dataIPShortCut->rNumericArgs(2);
186 54 : input_specs.maximum_outdoor_dry_bulb_temperature_for_crankcase_heater_operation = state.dataIPShortCut->rNumericArgs(3);
187 54 : if (state.dataIPShortCut->lNumericFieldBlanks(4)) {
188 52 : input_specs.unit_internal_static_air_pressure = 0.0;
189 : } else {
190 2 : input_specs.unit_internal_static_air_pressure = state.dataIPShortCut->rNumericArgs(4);
191 : }
192 54 : if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) {
193 0 : input_specs.outdoor_temperature_dependent_crankcase_heater_capacity_curve_name = state.dataIPShortCut->cAlphaArgs(2);
194 : }
195 54 : input_specs.capacity_control = state.dataIPShortCut->cAlphaArgs(3);
196 54 : input_specs.basin_heater_capacity = state.dataIPShortCut->rNumericArgs(5);
197 54 : input_specs.basin_heater_setpoint_temperature = state.dataIPShortCut->rNumericArgs(6);
198 54 : input_specs.basin_heater_operating_schedule_name = state.dataIPShortCut->cAlphaArgs(4);
199 54 : input_specs.compressor_fuel_type = state.dataIPShortCut->cAlphaArgs(5);
200 54 : input_specs.base_operating_mode_name = state.dataIPShortCut->cAlphaArgs(6);
201 54 : if (!state.dataIPShortCut->lAlphaFieldBlanks(6)) {
202 54 : input_specs.alternate_operating_mode_name = state.dataIPShortCut->cAlphaArgs(7);
203 : }
204 54 : if (!state.dataIPShortCut->lAlphaFieldBlanks(8)) {
205 1 : input_specs.alternate_operating_mode2_name = state.dataIPShortCut->cAlphaArgs(8);
206 : }
207 :
208 54 : this->instantiateFromInputSpec(state, input_specs);
209 54 : break;
210 54 : }
211 :
212 54 : if (!found_it) {
213 0 : ShowFatalError(state, format("Could not find Coil:Cooling:DX:Performance object with name: {}", name_to_find));
214 : }
215 54 : }
216 :
217 11222859 : void CoilCoolingDXCurveFitPerformance::simulate(EnergyPlus::EnergyPlusData &state,
218 : const DataLoopNode::NodeData &inletNode,
219 : DataLoopNode::NodeData &outletNode,
220 : HVAC::CoilMode currentCoilMode,
221 : int const speedNum,
222 : Real64 const speedRatio,
223 : HVAC::FanOp const fanOp,
224 : DataLoopNode::NodeData &condInletNode,
225 : DataLoopNode::NodeData &condOutletNode,
226 : bool const singleMode,
227 : Real64 LoadSHR)
228 : {
229 : static constexpr std::string_view RoutineName = "CoilCoolingDXCurveFitPerformance::simulate";
230 11222859 : Real64 reportingConstant = state.dataHVACGlobal->TimeStepSys * Constant::rSecsInHour;
231 11222859 : this->recoveredEnergyRate = 0.0;
232 11222859 : this->NormalSHR = 0.0;
233 :
234 11222859 : if (currentCoilMode == HVAC::CoilMode::SubcoolReheat) {
235 : Real64 totalCoolingRate;
236 : Real64 sensNorRate;
237 : Real64 sensSubRate;
238 : Real64 sensRehRate;
239 : Real64 latRate;
240 : Real64 SysNorSHR;
241 : Real64 SysSubSHR;
242 : Real64 SysRehSHR;
243 : Real64 HumRatNorOut;
244 : Real64 TempNorOut;
245 : Real64 EnthalpyNorOut;
246 : Real64 modeRatio;
247 :
248 76864 : this->calculate(state, this->normalMode, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
249 :
250 : // this->OperatingMode = 1;
251 76864 : CalcComponentSensibleLatentOutput(
252 76864 : outletNode.MassFlowRate, inletNode.Temp, inletNode.HumRat, outletNode.Temp, outletNode.HumRat, sensNorRate, latRate, totalCoolingRate);
253 76864 : if (totalCoolingRate > 1.0E-10) {
254 25616 : this->OperatingMode = 1;
255 25616 : this->NormalSHR = sensNorRate / totalCoolingRate;
256 25616 : this->powerUse = this->normalMode.OpModePower;
257 25616 : this->RTF = this->normalMode.OpModeRTF;
258 25616 : this->wasteHeatRate = this->normalMode.OpModeWasteHeat;
259 : }
260 :
261 76864 : if ((speedRatio != 0.0) && (LoadSHR != 0.0)) {
262 8184 : if (totalCoolingRate == 0.0) {
263 0 : SysNorSHR = 1.0;
264 : } else {
265 8184 : SysNorSHR = sensNorRate / totalCoolingRate;
266 : }
267 8184 : HumRatNorOut = outletNode.HumRat;
268 8184 : TempNorOut = outletNode.Temp;
269 8184 : EnthalpyNorOut = outletNode.Enthalpy;
270 8184 : this->recoveredEnergyRate = sensNorRate;
271 :
272 8184 : if (LoadSHR < SysNorSHR) {
273 5422 : outletNode.MassFlowRate = inletNode.MassFlowRate;
274 5422 : this->calculate(
275 5422 : state, this->alternateMode, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
276 5422 : CalcComponentSensibleLatentOutput(outletNode.MassFlowRate,
277 5422 : inletNode.Temp,
278 5422 : inletNode.HumRat,
279 : outletNode.Temp,
280 : outletNode.HumRat,
281 : sensSubRate,
282 : latRate,
283 : totalCoolingRate);
284 5422 : SysSubSHR = sensSubRate / totalCoolingRate;
285 5422 : if (LoadSHR < SysSubSHR) {
286 5422 : outletNode.MassFlowRate = inletNode.MassFlowRate;
287 5422 : this->calculate(
288 5422 : state, this->alternateMode2, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
289 5422 : CalcComponentSensibleLatentOutput(outletNode.MassFlowRate,
290 5422 : inletNode.Temp,
291 5422 : inletNode.HumRat,
292 : outletNode.Temp,
293 : outletNode.HumRat,
294 : sensRehRate,
295 : latRate,
296 : totalCoolingRate);
297 5422 : SysRehSHR = sensRehRate / totalCoolingRate;
298 5422 : if (LoadSHR > SysRehSHR) {
299 4686 : modeRatio = (LoadSHR - SysNorSHR) / (SysRehSHR - SysNorSHR);
300 4686 : this->OperatingMode = 3;
301 4686 : outletNode.HumRat = HumRatNorOut * (1.0 - modeRatio) + modeRatio * outletNode.HumRat;
302 4686 : outletNode.Enthalpy = EnthalpyNorOut * (1.0 - modeRatio) + modeRatio * outletNode.Enthalpy;
303 4686 : outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
304 4686 : this->ModeRatio = modeRatio;
305 : // update other reporting terms
306 4686 : this->powerUse = this->normalMode.OpModePower * (1.0 - modeRatio) + modeRatio * this->alternateMode2.OpModePower;
307 4686 : this->RTF = this->normalMode.OpModeRTF * (1.0 - modeRatio) + modeRatio * this->alternateMode2.OpModeRTF;
308 4686 : this->wasteHeatRate = this->normalMode.OpModeWasteHeat * (1.0 - modeRatio) + modeRatio * this->alternateMode2.OpModeWasteHeat;
309 4686 : this->recoveredEnergyRate = (this->recoveredEnergyRate - sensRehRate) * this->ModeRatio;
310 : } else {
311 736 : this->ModeRatio = 1.0;
312 736 : this->OperatingMode = 3;
313 736 : this->recoveredEnergyRate = (this->recoveredEnergyRate - sensRehRate) * this->ModeRatio;
314 : }
315 : } else {
316 0 : modeRatio = (LoadSHR - SysNorSHR) / (SysSubSHR - SysNorSHR);
317 0 : this->OperatingMode = 2;
318 : // process outlet conditions and total output
319 0 : outletNode.HumRat = HumRatNorOut * (1.0 - modeRatio) + modeRatio * outletNode.HumRat;
320 0 : outletNode.Enthalpy = EnthalpyNorOut * (1.0 - modeRatio) + modeRatio * outletNode.Enthalpy;
321 0 : outletNode.Temp = Psychrometrics::PsyTdbFnHW(outletNode.Enthalpy, outletNode.HumRat);
322 0 : this->ModeRatio = modeRatio;
323 : // update other reporting terms
324 0 : this->powerUse = this->normalMode.OpModePower * (1.0 - modeRatio) + modeRatio * this->alternateMode.OpModePower;
325 0 : this->RTF = this->normalMode.OpModeRTF * (1.0 - modeRatio) + modeRatio * this->alternateMode.OpModeRTF;
326 0 : this->wasteHeatRate = this->normalMode.OpModeWasteHeat * (1.0 - modeRatio) + modeRatio * this->alternateMode.OpModeWasteHeat;
327 0 : this->recoveredEnergyRate = (this->recoveredEnergyRate - sensSubRate) * this->ModeRatio;
328 : }
329 : } else {
330 2762 : this->ModeRatio = 0.0;
331 2762 : this->OperatingMode = 1;
332 2762 : this->recoveredEnergyRate = 0.0;
333 : }
334 : // Check for saturation error and modify temperature at constant enthalpy
335 8184 : Real64 tsat = Psychrometrics::PsyTsatFnHPb(state, outletNode.Enthalpy, inletNode.Press, RoutineName);
336 8184 : if (outletNode.Temp < tsat) {
337 0 : outletNode.Temp = tsat;
338 0 : outletNode.HumRat = Psychrometrics::PsyWFnTdbH(state, tsat, outletNode.Enthalpy);
339 : }
340 : }
341 11145995 : } else if (currentCoilMode == HVAC::CoilMode::Enhanced) {
342 0 : this->calculate(state, this->alternateMode, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
343 0 : this->OperatingMode = 2;
344 0 : this->powerUse = this->alternateMode.OpModePower;
345 0 : this->RTF = this->alternateMode.OpModeRTF;
346 0 : this->wasteHeatRate = this->alternateMode.OpModeWasteHeat;
347 : } else {
348 11145995 : this->calculate(state, this->normalMode, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
349 11145995 : this->OperatingMode = 1;
350 11145995 : this->powerUse = this->normalMode.OpModePower;
351 11145995 : this->RTF = this->normalMode.OpModeRTF;
352 11145995 : this->wasteHeatRate = this->normalMode.OpModeWasteHeat;
353 : }
354 :
355 : // calculate crankcase heater operation
356 11222859 : if (state.dataEnvrn->OutDryBulbTemp < this->maxOutdoorDrybulbForBasin) {
357 3705112 : this->crankcaseHeaterPower = this->crankcaseHeaterCap;
358 3705112 : if (this->crankcaseHeaterCapacityCurveIndex > 0) {
359 0 : this->crankcaseHeaterPower *= Curve::CurveValue(state, this->crankcaseHeaterCapacityCurveIndex, state.dataEnvrn->OutDryBulbTemp);
360 : }
361 : } else {
362 7517747 : this->crankcaseHeaterPower = 0.0;
363 : }
364 11222859 : this->crankcaseHeaterPower = this->crankcaseHeaterPower * (1.0 - this->RTF);
365 11222859 : this->crankcaseHeaterElectricityConsumption = this->crankcaseHeaterPower * reportingConstant;
366 :
367 : // basin heater
368 11222859 : if (this->evapCondBasinHeatSched != nullptr) {
369 11222859 : Real64 currentBasinHeaterAvail = this->evapCondBasinHeatSched->getCurrentVal();
370 11222859 : if (this->evapCondBasinHeatCap > 0.0 && currentBasinHeaterAvail > 0.0) {
371 0 : this->basinHeaterPower = max(0.0, this->evapCondBasinHeatCap * (this->evapCondBasinHeatSetpoint - state.dataEnvrn->OutDryBulbTemp));
372 : }
373 : } else {
374 : // If schedule does not exist, basin heater operates anytime outdoor dry-bulb temp is below setpoint
375 0 : if (this->evapCondBasinHeatCap > 0.0) {
376 0 : this->basinHeaterPower = max(0.0, this->evapCondBasinHeatCap * (this->evapCondBasinHeatSetpoint - state.dataEnvrn->OutDryBulbTemp));
377 : }
378 : }
379 11222859 : this->basinHeaterPower *= (1.0 - this->RTF);
380 11222859 : this->electricityConsumption = this->powerUse * reportingConstant;
381 :
382 11222859 : if (this->compressorFuelType != Constant::eFuel::Electricity) {
383 0 : this->compressorFuelRate = this->powerUse;
384 0 : this->compressorFuelConsumption = this->electricityConsumption;
385 :
386 : // check this after adding parasitic loads
387 0 : this->powerUse = 0.0;
388 0 : this->electricityConsumption = 0.0;
389 : }
390 11222859 : }
391 :
392 54 : void CoilCoolingDXCurveFitPerformance::size(EnergyPlus::EnergyPlusData &state)
393 : {
394 54 : if (!state.dataGlobal->SysSizingCalc && this->mySizeFlag) {
395 54 : this->normalMode.parentName = this->parentName;
396 54 : this->normalMode.size(state);
397 54 : if (this->maxAvailCoilMode == HVAC::CoilMode::Enhanced) {
398 5 : this->alternateMode.size(state);
399 : }
400 54 : if (this->maxAvailCoilMode == HVAC::CoilMode::SubcoolReheat) {
401 1 : this->alternateMode.size(state);
402 1 : this->alternateMode2.size(state);
403 : }
404 54 : this->mySizeFlag = false;
405 : }
406 54 : }
407 :
408 11233703 : void CoilCoolingDXCurveFitPerformance::calculate(EnergyPlus::EnergyPlusData &state,
409 : CoilCoolingDXCurveFitOperatingMode ¤tMode,
410 : const DataLoopNode::NodeData &inletNode,
411 : DataLoopNode::NodeData &outletNode,
412 : int const speedNum,
413 : Real64 const speedRatio,
414 : HVAC::FanOp const fanOp,
415 : DataLoopNode::NodeData &condInletNode,
416 : DataLoopNode::NodeData &condOutletNode,
417 : bool const singleMode)
418 : {
419 :
420 : // calculate the performance at this mode/speed
421 11233703 : currentMode.CalcOperatingMode(state, inletNode, outletNode, speedNum, speedRatio, fanOp, condInletNode, condOutletNode, singleMode);
422 11233703 : }
423 :
424 54 : void CoilCoolingDXCurveFitPerformance::calcStandardRatings210240(EnergyPlus::EnergyPlusData &state)
425 : {
426 :
427 : // for now this will provide standard ratings for the coil at the normal mode at speed N
428 : // future iterations will extend the inputs to give the user the flexibility to select different standards to
429 : // apply and such
430 :
431 54 : int constexpr NumOfReducedCap(4); // Number of reduced capacity test conditions (100%,75%,50%,and 25%)
432 :
433 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
434 54 : Real64 TotCapFlowModFac(0.0); // Total capacity modifier f(actual flow vs rated flow) for each speed [-]
435 54 : Real64 EIRFlowModFac(0.0); // EIR modifier f(actual supply air flow vs rated flow) for each speed [-]
436 54 : Real64 TotCapTempModFac(0.0); // Total capacity modifier (function of entering wetbulb, outside drybulb) [-]
437 54 : Real64 EIRTempModFac(0.0); // EIR modifier (function of entering wetbulb, outside drybulb) [-]
438 54 : Real64 TotCoolingCapAHRI(0.0); // Total Cooling Coil capacity (gross) at AHRI test conditions [W]
439 54 : Real64 NetCoolingCapAHRI(0.0); // Net Cooling Coil capacity at AHRI TestB conditions, accounting for fan heat [W]
440 54 : Real64 NetCoolingCapAHRI2023(0.0); // Net Cooling Coil capacity at AHRI TestB conditions, accounting for fan heat [W]
441 54 : Real64 TotalElecPower(0.0); // Net power consumption (Cond Fan+Compressor+Indoor Fan) at AHRI test conditions [W]
442 54 : Real64 TotalElecPower2023(0.0); // Net power consumption (Cond Fan+Compressor+Indoor Fan) at AHRI test conditions [W]
443 54 : Real64 TotalElecPowerRated(0.0); // Net power consumption (Cond Fan+Compressor+Indoor Fan) at Rated test conditions [W]
444 54 : Real64 TotalElecPowerRated2023(0.0); // Net power consumption (Cond Fan+Compressor+Indoor Fan) at Rated test conditions [W]
445 54 : Real64 EIR(0.0); // Energy Efficiency Ratio at AHRI test conditions for SEER [-]
446 54 : Real64 PartLoadFactor(0.0); // Part load factor, accounts for thermal lag at compressor startup [-]
447 54 : Real64 EERReduced(0.0); // EER at reduced capacity test conditions (100%, 75%, 50%, and 25%)
448 54 : Real64 ElecPowerReducedCap(0.0); // Net power consumption (Cond Fan+Compressor) at reduced test condition [W]
449 54 : Real64 NetCoolingCapReduced(0.0); // Net Cooling Coil capacity at reduced conditions, accounting for supply fan heat [W]
450 54 : Real64 LoadFactor(0.0); // Fractional "on" time for last stage at the desired reduced capacity, (dimensionless)
451 54 : Real64 DegradationCoeff(0.0); // Degradation coefficient, (dimensionless)
452 : Real64 OutdoorUnitInletAirDryBulbTempReduced; // Outdoor unit entering air dry-bulb temperature at reduced capacity [C]
453 :
454 : // *** SOME CONSTANTS FROM THE STANDARD
455 : // The AHRI standard specifies a nominal/default fan electric power consumption per rated air
456 : // volume flow rate to account for indoor fan electric power consumption
457 : // when the standard tests are conducted on units that do not have an
458 : // indoor air circulating fan. Used if user doesn't enter a specific value.
459 54 : Real64 constexpr DefaultFanPowerPerEvapAirFlowRate(773.3); // 365 W/1000 scfm or 773.3 W/(m3/s).
460 54 : Real64 constexpr DefaultFanPowerPerEvapAirFlowRate2023(934.4); // 441 W/1000 scfm or 934.4 W/(m3/s).
461 : // AHRI Standard 210/240-2008 Performance Test Conditions for Unitary Air-to-Air Air-Conditioning and Heat Pump Equipment
462 54 : Real64 constexpr CoolingCoilInletAirWetBulbTempRated(19.44); // 19.44C (67F) Tests A and B
463 54 : Real64 constexpr OutdoorUnitInletAirDryBulbTemp(27.78); // 27.78C (82F) Test B (for SEER)
464 54 : Real64 constexpr OutdoorUnitInletAirDryBulbTempRated(35.0); // 35.00C (95F) Test A (rated capacity)
465 54 : Real64 constexpr AirMassFlowRatioRated(1.0); // AHRI test is at the design flow rate so AirMassFlowRatio is 1.0
466 54 : Real64 constexpr PLRforSEER(0.5); // Part-load ratio for SEER calculation (single speed DX cooling coils)
467 : static constexpr std::array<Real64, 4> ReducedPLR = {1.0, 0.75, 0.50, 0.25}; // Reduced Capacity part-load conditions
468 : static constexpr std::array<Real64, 4> IEERWeightingFactor = {0.020, 0.617, 0.238, 0.125}; // EER Weighting factors (IEER)
469 54 : Real64 constexpr OADBTempLowReducedCapacityTest(18.3); // Outdoor air dry-bulb temp in degrees C (65F)
470 :
471 : // For Single Stage Systems, if the optional CFull and DFull tests are not performed, a
472 : // default value of 0.20 shall be used for the cooling Degradation Coefficient
473 54 : Real64 constexpr CyclicDegradationCoefficient(0.20); // ANSI/AHRI 210/240 2023 Section 6.1.3.1
474 :
475 : // some conveniences
476 54 : auto &mode = this->normalMode;
477 54 : auto &speed = mode.speeds.back();
478 :
479 54 : Real64 FanPowerPerEvapAirFlowRate = DefaultFanPowerPerEvapAirFlowRate;
480 54 : if (speed.rated_evap_fan_power_per_volume_flow_rate > 0.0) {
481 54 : FanPowerPerEvapAirFlowRate = speed.rated_evap_fan_power_per_volume_flow_rate;
482 : }
483 :
484 54 : Real64 FanPowerPerEvapAirFlowRate2023 = DefaultFanPowerPerEvapAirFlowRate2023;
485 54 : if (speed.rated_evap_fan_power_per_volume_flow_rate_2023 > 0.0) {
486 54 : FanPowerPerEvapAirFlowRate2023 = speed.rated_evap_fan_power_per_volume_flow_rate_2023;
487 : }
488 :
489 54 : if (mode.ratedGrossTotalCap > 0.0) {
490 54 : TotCapFlowModFac = Curve::CurveValue(state, speed.indexCapFFF, AirMassFlowRatioRated);
491 :
492 54 : TotCapTempModFac = Curve::CurveValue(state, speed.indexCapFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTemp);
493 54 : TotCoolingCapAHRI = mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac;
494 : // Calculate net cooling capacity | 2017
495 54 : this->standardRatingCoolingCapacity = TotCoolingCapAHRI - FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
496 : // Calculate net cooling capacity | 2023
497 54 : this->standardRatingCoolingCapacity2023 = TotCoolingCapAHRI - FanPowerPerEvapAirFlowRate2023 * mode.ratedEvapAirFlowRate;
498 : // TODO: Commercial and industrial unitary air-conditioning condensing units with a capacity greater than 135,000 Btu/h (39564.59445 Watts)
499 : // as defined in ANSI/AHRI Standard 365(I-P). | Scope 2.2.6 (ANSI/AHRI 340-360 2022)
500 :
501 : // SEER2 standard applies to factory-made Unitary Air-conditioners and Unitary Air-source Heat Pumps with
502 : // capacities less than 65,000 Btu/h (19049.61955 Watts) | Section 2.1 (ANSI/AHRI 210-240 2023)
503 : // Removal of water-cooled and evaporatively-cooled products from the scope | Foreword (ANSI/AHRI 210-240 2023)
504 :
505 : // SEER calculations:
506 54 : TotCapTempModFac = Curve::CurveValue(state, speed.indexCapFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTemp);
507 54 : TotCoolingCapAHRI = mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac;
508 54 : EIRTempModFac = Curve::CurveValue(state, speed.indexEIRFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTemp);
509 54 : EIRFlowModFac = Curve::CurveValue(state, speed.indexEIRFFF, AirMassFlowRatioRated);
510 54 : if (speed.ratedCOP > 0.0) { // RatedCOP <= 0.0 is trapped in GetInput, but keep this as "safety"
511 54 : EIR = EIRTempModFac * EIRFlowModFac / speed.ratedCOP;
512 : } else {
513 0 : EIR = 0.0;
514 : }
515 :
516 : // Calculate net cooling capacity
517 54 : NetCoolingCapAHRI = TotCoolingCapAHRI - FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
518 54 : TotalElecPower = EIR * TotCoolingCapAHRI + FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
519 :
520 54 : NetCoolingCapAHRI2023 = TotCoolingCapAHRI - FanPowerPerEvapAirFlowRate2023 * mode.ratedEvapAirFlowRate;
521 54 : TotalElecPower2023 = EIR * TotCoolingCapAHRI + FanPowerPerEvapAirFlowRate2023 * mode.ratedEvapAirFlowRate;
522 : // Calculate SEER value from the Energy Efficiency Ratio (EER) at the AHRI test conditions and the part load factor.
523 : // First evaluate the Part Load Factor curve at PLR = 0.5 (AHRI Standard 210/240)
524 54 : PartLoadFactor = Curve::CurveValue(state, speed.indexPLRFPLF, PLRforSEER);
525 54 : Real64 PartLoadFactorStandard = 1.0 - (1 - PLRforSEER) * CyclicDegradationCoefficient;
526 54 : if (TotalElecPower > 0.0) {
527 54 : this->standardRatingSEER = (NetCoolingCapAHRI / TotalElecPower) * PartLoadFactor;
528 54 : this->standardRatingSEER_Standard = (NetCoolingCapAHRI / TotalElecPower) * PartLoadFactorStandard;
529 : } else {
530 0 : this->standardRatingSEER = 0.0;
531 0 : this->standardRatingSEER2_Standard = 0.0;
532 : }
533 :
534 54 : if (TotalElecPower2023 > 0.0) {
535 54 : this->standardRatingSEER2_User = (NetCoolingCapAHRI2023 / TotalElecPower2023) * PartLoadFactor;
536 54 : this->standardRatingSEER2_Standard = (NetCoolingCapAHRI2023 / TotalElecPower2023) * PartLoadFactorStandard;
537 : } else {
538 0 : this->standardRatingSEER2_User = 0.0;
539 0 : this->standardRatingSEER2_Standard = 0.0;
540 : }
541 :
542 : // EER calculations:
543 : // Calculate the net cooling capacity at the rated conditions (19.44C WB and 35.0C DB )
544 54 : TotCapTempModFac = Curve::CurveValue(state, speed.indexCapFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTempRated);
545 54 : this->standardRatingCoolingCapacity =
546 54 : mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac - FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
547 54 : this->standardRatingCoolingCapacity2023 =
548 54 : mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac - FanPowerPerEvapAirFlowRate2023 * mode.ratedEvapAirFlowRate;
549 : // Calculate Energy Efficiency Ratio (EER) at (19.44C WB and 35.0C DB ), ANSI/AHRI Std. 340/360
550 54 : EIRTempModFac = Curve::CurveValue(state, speed.indexEIRFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTempRated);
551 54 : if (speed.ratedCOP > 0.0) {
552 : // RatedCOP <= 0.0 is trapped in GetInput, but keep this as "safety"
553 54 : EIR = EIRTempModFac * EIRFlowModFac / speed.ratedCOP;
554 : } else {
555 0 : EIR = 0.0;
556 : }
557 54 : TotalElecPowerRated =
558 54 : EIR * (mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac) + FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
559 54 : if (TotalElecPowerRated > 0.0) {
560 54 : this->standardRatingEER = this->standardRatingCoolingCapacity / TotalElecPowerRated;
561 : } else {
562 0 : this->standardRatingEER = 0.0;
563 : }
564 54 : TotalElecPowerRated2023 =
565 54 : EIR * (mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac) + FanPowerPerEvapAirFlowRate2023 * mode.ratedEvapAirFlowRate;
566 54 : if (TotalElecPowerRated2023 > 0.0) {
567 54 : this->standardRatingEER2 = this->standardRatingCoolingCapacity2023 / TotalElecPowerRated2023;
568 : } else {
569 0 : this->standardRatingEER2 = 0.0;
570 : }
571 :
572 54 : if (mode.condenserType == CoilCoolingDXCurveFitOperatingMode::CondenserType::AIRCOOLED) {
573 108 : std::tie(this->standardRatingCoolingCapacity2023,
574 54 : this->standardRatingSEER2_User,
575 54 : this->standardRatingSEER2_Standard,
576 162 : this->standardRatingEER2) = StandardRatings::SEER2CalulcationCurveFit(state, "Coil:Cooling:DX:CurveFit", this->normalMode);
577 : }
578 :
579 : // IEER calculations: Capacity of 65K Btu/h (19050 W) to less than 135K Btu/h (39565 W) - calculated as per AHRI Standard 340/360-2022.
580 54 : this->standardRatingIEER = 0.0;
581 : // Calculate the net cooling capacity at the rated conditions (19.44C WB and 35.0C DB )
582 54 : TotCapTempModFac = Curve::CurveValue(state, speed.indexCapFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTempRated);
583 54 : this->standardRatingCoolingCapacity =
584 54 : mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac - FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
585 270 : for (int RedCapNum = 0; RedCapNum < NumOfReducedCap; ++RedCapNum) {
586 : // get the outdoor air dry bulb temperature for the reduced capacity test conditions
587 216 : if (ReducedPLR[RedCapNum] > 0.444) {
588 162 : OutdoorUnitInletAirDryBulbTempReduced = 5.0 + 30.0 * ReducedPLR[RedCapNum];
589 : } else {
590 54 : OutdoorUnitInletAirDryBulbTempReduced = OADBTempLowReducedCapacityTest;
591 : }
592 216 : TotCapTempModFac = Curve::CurveValue(state, speed.indexCapFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTempReduced);
593 216 : NetCoolingCapReduced =
594 216 : mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac - FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate;
595 216 : EIRTempModFac = Curve::CurveValue(state, speed.indexEIRFT, CoolingCoilInletAirWetBulbTempRated, OutdoorUnitInletAirDryBulbTempReduced);
596 216 : EIRFlowModFac = Curve::CurveValue(state, speed.indexEIRFFF, AirMassFlowRatioRated);
597 216 : if (speed.ratedCOP > 0.0) {
598 216 : EIR = EIRTempModFac * EIRFlowModFac / speed.ratedCOP;
599 : } else {
600 0 : EIR = 0.0;
601 : }
602 216 : if (NetCoolingCapReduced > 0.0) {
603 216 : LoadFactor = ReducedPLR[RedCapNum] * this->standardRatingCoolingCapacity / NetCoolingCapReduced;
604 : } else {
605 0 : LoadFactor = 1.0;
606 : }
607 216 : DegradationCoeff = 1.130 - 0.130 * LoadFactor;
608 216 : ElecPowerReducedCap = DegradationCoeff * EIR * (mode.ratedGrossTotalCap * TotCapTempModFac * TotCapFlowModFac);
609 216 : EERReduced =
610 216 : (LoadFactor * NetCoolingCapReduced) / (LoadFactor * ElecPowerReducedCap + FanPowerPerEvapAirFlowRate * mode.ratedEvapAirFlowRate);
611 216 : this->standardRatingIEER += IEERWeightingFactor[RedCapNum] * EERReduced;
612 : }
613 :
614 : // IEER 2022 -->
615 : // TODO: we can always decide and give precedence to Alternate Mode 1 or Alternate Mode 2 if present | Needs Discussion about the
616 : // applicability.
617 54 : std::tie(this->standardRatingIEER2, this->standardRatingCoolingCapacity2023, this->standardRatingEER2) =
618 216 : StandardRatings::IEERCalulcationCurveFit(state, "Coil:Cooling:DX:CurveFit", this->normalMode);
619 :
620 : } else {
621 0 : ShowSevereError(
622 : state,
623 0 : format("Standard Ratings: Coil:Cooling:DX {} has zero rated total cooling capacity. Standard ratings cannot be calculated.", this->name));
624 : }
625 54 : }
626 :
627 3 : void CoilCoolingDXCurveFitPerformance::setOperMode(EnergyPlus::EnergyPlusData &state, CoilCoolingDXCurveFitOperatingMode ¤tMode, int const mode)
628 : {
629 : // set parent mode for each speed
630 : int numSpeeds;
631 3 : bool errorsFound = false;
632 :
633 3 : numSpeeds = (int)currentMode.speeds.size();
634 6 : for (int speedNum = 0; speedNum < numSpeeds; speedNum++) {
635 3 : currentMode.speeds[speedNum].parentOperatingMode = mode;
636 3 : if (mode == 2) {
637 1 : if (currentMode.speeds[speedNum].indexSHRFT == 0) {
638 0 : ShowSevereError(state,
639 0 : format("{}=\"{}\", Curve check:", currentMode.speeds[speedNum].object_name, currentMode.speeds[speedNum].name));
640 0 : ShowContinueError(state,
641 : "The input of Sensible Heat Ratio Modifier Function of Temperature Curve Name is required, but not available for "
642 : "SubcoolReheat mode. Please input");
643 0 : errorsFound = true;
644 : }
645 1 : if (currentMode.speeds[speedNum].indexSHRFFF == 0) {
646 0 : ShowSevereError(state,
647 0 : format("{}=\"{}\", Curve check:", currentMode.speeds[speedNum].object_name, currentMode.speeds[speedNum].name));
648 0 : ShowContinueError(state,
649 : "The input of Sensible Heat Ratio Modifier Function of Flow Fraction Curve Name is required, but not available for "
650 : "SubcoolReheat mode. Please input");
651 0 : errorsFound = true;
652 : }
653 : }
654 3 : if (mode == 3) {
655 1 : if (currentMode.speeds[speedNum].indexSHRFT == 0) {
656 0 : ShowSevereError(state,
657 0 : format("{}=\"{}\", Curve check:", currentMode.speeds[speedNum].object_name, currentMode.speeds[speedNum].name));
658 0 : ShowContinueError(state,
659 : "The input of Sensible Heat Ratio Modifier Function of Temperature Curve Name is required, but not available for "
660 : "SubcoolReheat mode. Please input");
661 0 : errorsFound = true;
662 : }
663 1 : if (currentMode.speeds[speedNum].indexSHRFFF == 0) {
664 0 : ShowSevereError(state,
665 0 : format("{}=\"{}\", Curve check:", currentMode.speeds[speedNum].object_name, currentMode.speeds[speedNum].name));
666 0 : ShowContinueError(state,
667 : "The input of Sensible Heat Ratio Modifier Function of Flow Fraction Curve Name is required, but not available for "
668 : "SubcoolReheat mode. Please input");
669 0 : errorsFound = true;
670 : }
671 : }
672 : }
673 3 : if (errorsFound) {
674 0 : ShowFatalError(state,
675 0 : format("CoilCoolingDXCurveFitPerformance: Errors found in getting {} input. Preceding condition(s) causes termination.",
676 : this->object_name));
677 : }
678 3 : }
|